Configure GCP Data Catalog Entries

The gcp:datacatalog/entry:Entry resource, part of the Pulumi GCP provider, registers resources in Data Catalog with metadata, schemas, and type information. Data Catalog is deprecated and will be discontinued on January 30, 2026; for migration guidance, see the Dataplex Catalog transition documentation. This guide focuses on three capabilities: custom type registration for external systems, Cloud Storage fileset cataloging, and schema documentation.

Entries belong to entry groups and may reference Cloud Storage buckets or external systems that exist outside the catalog. The examples are intentionally small. Combine them with your own entry group organization and metadata standards.

Catalog external resources with custom types

Teams cataloging resources outside Google Cloud register them with custom type identifiers that describe the external system.

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

const entryGroup = new gcp.datacatalog.EntryGroup("entry_group", {entryGroupId: "my_group"});
const basicEntry = new gcp.datacatalog.Entry("basic_entry", {
    entryGroup: entryGroup.id,
    entryId: "my_entry",
    userSpecifiedType: "my_custom_type",
    userSpecifiedSystem: "SomethingExternal",
});
import pulumi
import pulumi_gcp as gcp

entry_group = gcp.datacatalog.EntryGroup("entry_group", entry_group_id="my_group")
basic_entry = gcp.datacatalog.Entry("basic_entry",
    entry_group=entry_group.id,
    entry_id="my_entry",
    user_specified_type="my_custom_type",
    user_specified_system="SomethingExternal")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		entryGroup, err := datacatalog.NewEntryGroup(ctx, "entry_group", &datacatalog.EntryGroupArgs{
			EntryGroupId: pulumi.String("my_group"),
		})
		if err != nil {
			return err
		}
		_, err = datacatalog.NewEntry(ctx, "basic_entry", &datacatalog.EntryArgs{
			EntryGroup:          entryGroup.ID(),
			EntryId:             pulumi.String("my_entry"),
			UserSpecifiedType:   pulumi.String("my_custom_type"),
			UserSpecifiedSystem: pulumi.String("SomethingExternal"),
		})
		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 entryGroup = new Gcp.DataCatalog.EntryGroup("entry_group", new()
    {
        EntryGroupId = "my_group",
    });

    var basicEntry = new Gcp.DataCatalog.Entry("basic_entry", new()
    {
        EntryGroup = entryGroup.Id,
        EntryId = "my_entry",
        UserSpecifiedType = "my_custom_type",
        UserSpecifiedSystem = "SomethingExternal",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.datacatalog.EntryGroup;
import com.pulumi.gcp.datacatalog.EntryGroupArgs;
import com.pulumi.gcp.datacatalog.Entry;
import com.pulumi.gcp.datacatalog.EntryArgs;
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 entryGroup = new EntryGroup("entryGroup", EntryGroupArgs.builder()
            .entryGroupId("my_group")
            .build());

        var basicEntry = new Entry("basicEntry", EntryArgs.builder()
            .entryGroup(entryGroup.id())
            .entryId("my_entry")
            .userSpecifiedType("my_custom_type")
            .userSpecifiedSystem("SomethingExternal")
            .build());

    }
}
resources:
  basicEntry:
    type: gcp:datacatalog:Entry
    name: basic_entry
    properties:
      entryGroup: ${entryGroup.id}
      entryId: my_entry
      userSpecifiedType: my_custom_type
      userSpecifiedSystem: SomethingExternal
  entryGroup:
    type: gcp:datacatalog:EntryGroup
    name: entry_group
    properties:
      entryGroupId: my_group

The userSpecifiedType and userSpecifiedSystem properties label the entry with custom identifiers when the resource doesn’t match Data Catalog’s built-in types. The entryGroup places the entry in an organizational container, and entryId provides a unique identifier within that group.

Catalog Cloud Storage filesets with patterns

Data pipelines working with file collections in Cloud Storage can track them as single logical entities using fileset entries.

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

const entryGroup = new gcp.datacatalog.EntryGroup("entry_group", {entryGroupId: "my_group"});
const basicEntry = new gcp.datacatalog.Entry("basic_entry", {
    entryGroup: entryGroup.id,
    entryId: "my_entry",
    type: "FILESET",
    gcsFilesetSpec: {
        filePatterns: ["gs://fake_bucket/dir/*"],
    },
});
import pulumi
import pulumi_gcp as gcp

entry_group = gcp.datacatalog.EntryGroup("entry_group", entry_group_id="my_group")
basic_entry = gcp.datacatalog.Entry("basic_entry",
    entry_group=entry_group.id,
    entry_id="my_entry",
    type="FILESET",
    gcs_fileset_spec={
        "file_patterns": ["gs://fake_bucket/dir/*"],
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		entryGroup, err := datacatalog.NewEntryGroup(ctx, "entry_group", &datacatalog.EntryGroupArgs{
			EntryGroupId: pulumi.String("my_group"),
		})
		if err != nil {
			return err
		}
		_, err = datacatalog.NewEntry(ctx, "basic_entry", &datacatalog.EntryArgs{
			EntryGroup: entryGroup.ID(),
			EntryId:    pulumi.String("my_entry"),
			Type:       pulumi.String("FILESET"),
			GcsFilesetSpec: &datacatalog.EntryGcsFilesetSpecArgs{
				FilePatterns: pulumi.StringArray{
					pulumi.String("gs://fake_bucket/dir/*"),
				},
			},
		})
		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 entryGroup = new Gcp.DataCatalog.EntryGroup("entry_group", new()
    {
        EntryGroupId = "my_group",
    });

    var basicEntry = new Gcp.DataCatalog.Entry("basic_entry", new()
    {
        EntryGroup = entryGroup.Id,
        EntryId = "my_entry",
        Type = "FILESET",
        GcsFilesetSpec = new Gcp.DataCatalog.Inputs.EntryGcsFilesetSpecArgs
        {
            FilePatterns = new[]
            {
                "gs://fake_bucket/dir/*",
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.datacatalog.EntryGroup;
import com.pulumi.gcp.datacatalog.EntryGroupArgs;
import com.pulumi.gcp.datacatalog.Entry;
import com.pulumi.gcp.datacatalog.EntryArgs;
import com.pulumi.gcp.datacatalog.inputs.EntryGcsFilesetSpecArgs;
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 entryGroup = new EntryGroup("entryGroup", EntryGroupArgs.builder()
            .entryGroupId("my_group")
            .build());

        var basicEntry = new Entry("basicEntry", EntryArgs.builder()
            .entryGroup(entryGroup.id())
            .entryId("my_entry")
            .type("FILESET")
            .gcsFilesetSpec(EntryGcsFilesetSpecArgs.builder()
                .filePatterns("gs://fake_bucket/dir/*")
                .build())
            .build());

    }
}
resources:
  basicEntry:
    type: gcp:datacatalog:Entry
    name: basic_entry
    properties:
      entryGroup: ${entryGroup.id}
      entryId: my_entry
      type: FILESET
      gcsFilesetSpec:
        filePatterns:
          - gs://fake_bucket/dir/*
  entryGroup:
    type: gcp:datacatalog:EntryGroup
    name: entry_group
    properties:
      entryGroupId: my_group

The type property set to FILESET tells Data Catalog this entry represents a collection of files. The gcsFilesetSpec.filePatterns property uses glob patterns to define which files belong to the set, allowing wildcards to match multiple objects.

Document entries with schema and metadata

Production catalogs need rich metadata including schemas, descriptions, and links to help users understand data contents and origins.

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

const entryGroup = new gcp.datacatalog.EntryGroup("entry_group", {entryGroupId: "my_group"});
const basicEntry = new gcp.datacatalog.Entry("basic_entry", {
    entryGroup: entryGroup.id,
    entryId: "my_entry",
    userSpecifiedType: "my_user_specified_type",
    userSpecifiedSystem: "Something_custom",
    linkedResource: "my/linked/resource",
    displayName: "my custom type entry",
    description: "a custom type entry for a user specified system",
    schema: `{
  \\"columns\\": [
    {
      \\"column\\": \\"first_name\\",
      \\"description\\": \\"First name\\",
      \\"mode\\": \\"REQUIRED\\",
      \\"type\\": \\"STRING\\"
    },
    {
      \\"column\\": \\"last_name\\",
      \\"description\\": \\"Last name\\",
      \\"mode\\": \\"REQUIRED\\",
      \\"type\\": \\"STRING\\"
    },
    {
      \\"column\\": \\"address\\",
      \\"description\\": \\"Address\\",
      \\"mode\\": \\"REPEATED\\",
      \\"subcolumns\\": [
        {
          \\"column\\": \\"city\\",
          \\"description\\": \\"City\\",
          \\"mode\\": \\"NULLABLE\\",
          \\"type\\": \\"STRING\\"
        },
        {
          \\"column\\": \\"state\\",
          \\"description\\": \\"State\\",
          \\"mode\\": \\"NULLABLE\\",
          \\"type\\": \\"STRING\\"
        }
      ],
      \\"type\\": \\"RECORD\\"
    }
  ]
}
`,
});
import pulumi
import pulumi_gcp as gcp

entry_group = gcp.datacatalog.EntryGroup("entry_group", entry_group_id="my_group")
basic_entry = gcp.datacatalog.Entry("basic_entry",
    entry_group=entry_group.id,
    entry_id="my_entry",
    user_specified_type="my_user_specified_type",
    user_specified_system="Something_custom",
    linked_resource="my/linked/resource",
    display_name="my custom type entry",
    description="a custom type entry for a user specified system",
    schema="""{
  \"columns\": [
    {
      \"column\": \"first_name\",
      \"description\": \"First name\",
      \"mode\": \"REQUIRED\",
      \"type\": \"STRING\"
    },
    {
      \"column\": \"last_name\",
      \"description\": \"Last name\",
      \"mode\": \"REQUIRED\",
      \"type\": \"STRING\"
    },
    {
      \"column\": \"address\",
      \"description\": \"Address\",
      \"mode\": \"REPEATED\",
      \"subcolumns\": [
        {
          \"column\": \"city\",
          \"description\": \"City\",
          \"mode\": \"NULLABLE\",
          \"type\": \"STRING\"
        },
        {
          \"column\": \"state\",
          \"description\": \"State\",
          \"mode\": \"NULLABLE\",
          \"type\": \"STRING\"
        }
      ],
      \"type\": \"RECORD\"
    }
  ]
}
""")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		entryGroup, err := datacatalog.NewEntryGroup(ctx, "entry_group", &datacatalog.EntryGroupArgs{
			EntryGroupId: pulumi.String("my_group"),
		})
		if err != nil {
			return err
		}
		_, err = datacatalog.NewEntry(ctx, "basic_entry", &datacatalog.EntryArgs{
			EntryGroup:          entryGroup.ID(),
			EntryId:             pulumi.String("my_entry"),
			UserSpecifiedType:   pulumi.String("my_user_specified_type"),
			UserSpecifiedSystem: pulumi.String("Something_custom"),
			LinkedResource:      pulumi.String("my/linked/resource"),
			DisplayName:         pulumi.String("my custom type entry"),
			Description:         pulumi.String("a custom type entry for a user specified system"),
			Schema: pulumi.String(`{
  \"columns\": [
    {
      \"column\": \"first_name\",
      \"description\": \"First name\",
      \"mode\": \"REQUIRED\",
      \"type\": \"STRING\"
    },
    {
      \"column\": \"last_name\",
      \"description\": \"Last name\",
      \"mode\": \"REQUIRED\",
      \"type\": \"STRING\"
    },
    {
      \"column\": \"address\",
      \"description\": \"Address\",
      \"mode\": \"REPEATED\",
      \"subcolumns\": [
        {
          \"column\": \"city\",
          \"description\": \"City\",
          \"mode\": \"NULLABLE\",
          \"type\": \"STRING\"
        },
        {
          \"column\": \"state\",
          \"description\": \"State\",
          \"mode\": \"NULLABLE\",
          \"type\": \"STRING\"
        }
      ],
      \"type\": \"RECORD\"
    }
  ]
}
`),
		})
		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 entryGroup = new Gcp.DataCatalog.EntryGroup("entry_group", new()
    {
        EntryGroupId = "my_group",
    });

    var basicEntry = new Gcp.DataCatalog.Entry("basic_entry", new()
    {
        EntryGroup = entryGroup.Id,
        EntryId = "my_entry",
        UserSpecifiedType = "my_user_specified_type",
        UserSpecifiedSystem = "Something_custom",
        LinkedResource = "my/linked/resource",
        DisplayName = "my custom type entry",
        Description = "a custom type entry for a user specified system",
        Schema = @"{
  \""columns\"": [
    {
      \""column\"": \""first_name\"",
      \""description\"": \""First name\"",
      \""mode\"": \""REQUIRED\"",
      \""type\"": \""STRING\""
    },
    {
      \""column\"": \""last_name\"",
      \""description\"": \""Last name\"",
      \""mode\"": \""REQUIRED\"",
      \""type\"": \""STRING\""
    },
    {
      \""column\"": \""address\"",
      \""description\"": \""Address\"",
      \""mode\"": \""REPEATED\"",
      \""subcolumns\"": [
        {
          \""column\"": \""city\"",
          \""description\"": \""City\"",
          \""mode\"": \""NULLABLE\"",
          \""type\"": \""STRING\""
        },
        {
          \""column\"": \""state\"",
          \""description\"": \""State\"",
          \""mode\"": \""NULLABLE\"",
          \""type\"": \""STRING\""
        }
      ],
      \""type\"": \""RECORD\""
    }
  ]
}
",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.datacatalog.EntryGroup;
import com.pulumi.gcp.datacatalog.EntryGroupArgs;
import com.pulumi.gcp.datacatalog.Entry;
import com.pulumi.gcp.datacatalog.EntryArgs;
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 entryGroup = new EntryGroup("entryGroup", EntryGroupArgs.builder()
            .entryGroupId("my_group")
            .build());

        var basicEntry = new Entry("basicEntry", EntryArgs.builder()
            .entryGroup(entryGroup.id())
            .entryId("my_entry")
            .userSpecifiedType("my_user_specified_type")
            .userSpecifiedSystem("Something_custom")
            .linkedResource("my/linked/resource")
            .displayName("my custom type entry")
            .description("a custom type entry for a user specified system")
            .schema("""
{
  \"columns\": [
    {
      \"column\": \"first_name\",
      \"description\": \"First name\",
      \"mode\": \"REQUIRED\",
      \"type\": \"STRING\"
    },
    {
      \"column\": \"last_name\",
      \"description\": \"Last name\",
      \"mode\": \"REQUIRED\",
      \"type\": \"STRING\"
    },
    {
      \"column\": \"address\",
      \"description\": \"Address\",
      \"mode\": \"REPEATED\",
      \"subcolumns\": [
        {
          \"column\": \"city\",
          \"description\": \"City\",
          \"mode\": \"NULLABLE\",
          \"type\": \"STRING\"
        },
        {
          \"column\": \"state\",
          \"description\": \"State\",
          \"mode\": \"NULLABLE\",
          \"type\": \"STRING\"
        }
      ],
      \"type\": \"RECORD\"
    }
  ]
}
            """)
            .build());

    }
}
resources:
  basicEntry:
    type: gcp:datacatalog:Entry
    name: basic_entry
    properties:
      entryGroup: ${entryGroup.id}
      entryId: my_entry
      userSpecifiedType: my_user_specified_type
      userSpecifiedSystem: Something_custom
      linkedResource: my/linked/resource
      displayName: my custom type entry
      description: a custom type entry for a user specified system
      schema: |
        {
          \"columns\": [
            {
              \"column\": \"first_name\",
              \"description\": \"First name\",
              \"mode\": \"REQUIRED\",
              \"type\": \"STRING\"
            },
            {
              \"column\": \"last_name\",
              \"description\": \"Last name\",
              \"mode\": \"REQUIRED\",
              \"type\": \"STRING\"
            },
            {
              \"column\": \"address\",
              \"description\": \"Address\",
              \"mode\": \"REPEATED\",
              \"subcolumns\": [
                {
                  \"column\": \"city\",
                  \"description\": \"City\",
                  \"mode\": \"NULLABLE\",
                  \"type\": \"STRING\"
                },
                {
                  \"column\": \"state\",
                  \"description\": \"State\",
                  \"mode\": \"NULLABLE\",
                  \"type\": \"STRING\"
                }
              ],
              \"type\": \"RECORD\"
            }
          ]
        }        
  entryGroup:
    type: gcp:datacatalog:EntryGroup
    name: entry_group
    properties:
      entryGroupId: my_group

The schema property contains a JSON string describing the data structure with columns, types, and nested records. The displayName and description properties provide human-readable documentation. The linkedResource property connects the catalog entry back to the actual resource in its source system.

Beyond these examples

These snippets focus on specific entry-level features: custom type registration for external systems, Cloud Storage fileset cataloging, and schema documentation and metadata. They’re intentionally minimal rather than full catalog implementations.

The examples may reference pre-existing infrastructure such as Data Catalog entry groups (created inline in examples) and Cloud Storage buckets (referenced but not created). They focus on entry configuration rather than provisioning the surrounding catalog structure.

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

  • BigQuery table integration (integratedSystem output)
  • Tag attachment for flexible metadata
  • Entry group organization and permissions
  • Linked resource validation and format requirements

These omissions are intentional: the goal is to illustrate how each entry feature is wired, not provide drop-in catalog modules. See the Data Catalog Entry resource reference for all available configuration options.

Let's configure GCP Data Catalog Entries

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Deprecation & Migration
Is Data Catalog Entry still supported?
No, gcp.datacatalog.Entry is deprecated and will be removed in a future major release. Data Catalog will be discontinued on January 30, 2026. You should transition to Dataplex Catalog using the migration guide at https://cloud.google.com/dataplex/docs/transition-to-dataplex-catalog.
Entry Types & Configuration
What's the difference between type and userSpecifiedType?
Use type only for FILESET entries (the only allowed value from the EntryType enum). For all other entry types, use userSpecifiedType with a custom value since the enum is limited to FILESET.
When should I use FILESET vs a custom entry type?
Use type: "FILESET" with gcsFilesetSpec for Cloud Storage filesets. For BigQuery tables, Pub/Sub topics, or external resources, use userSpecifiedType with a custom type name.
How do I catalog resources outside Google Cloud Platform?
Set userSpecifiedSystem to indicate the external source system (e.g., “SomethingExternal”) and use userSpecifiedType for the custom entry type. The linkedResource field is optional for these entries.
What format does the schema field use?
The schema field accepts a JSON string with a columns array. Each column can specify column, description, mode (REQUIRED/NULLABLE/REPEATED), type, and nested subcolumns for RECORD types.
Immutability & Constraints
What properties can't I change after creating an entry?
The entryGroup, entryId, and type properties are immutable and cannot be changed after creation.
What are the naming rules for custom types and systems?
Both userSpecifiedType and userSpecifiedSystem must begin with a letter or underscore, contain only letters, numbers, and underscores, are case insensitive, and must be 1-64 characters long.
Resource References
What should I put in the linkedResource field?
For GCP resources, use the full resource name (e.g., //bigquery.googleapis.com/projects/projectId/datasets/datasetId/tables/tableId). For entries with userSpecifiedType, this field is optional and defaults to an empty string.

Using a different cloud?

Explore analytics guides for other cloud providers: