Configure GCP Data Catalog Entries

The gcp:datacatalog/entry:Entry resource, part of the Pulumi GCP provider, represents resources in Google Cloud or external systems as catalog entries with metadata, schema, and searchable descriptions. 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: external resource registration with custom types, Cloud Storage fileset cataloging, and schema definition with descriptive metadata.

Entries belong to entry groups and may reference Cloud Storage buckets or external systems. The examples are intentionally small. Combine them with your own entry groups and resource references.

Register external resources with custom types

Teams cataloging resources outside Google Cloud create entries that reference external systems as metadata containers.

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 identify resources from non-integrated systems. The entryGroup property places the entry in an organizational container. This creates a catalog record without schema or detailed metadata.

Catalog Cloud Storage filesets with patterns

Data pipelines working with file collections in Cloud Storage can catalog them as single logical units 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” enables Cloud Storage-specific configuration. The gcsFilesetSpec property defines file patterns that match collections of objects. This lets you catalog directories or pattern-matched files as unified datasets.

Add schema and metadata to catalog entries

Entries become searchable and useful when they include schema definitions and descriptive metadata.

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 accepts JSON that describes columns, types, modes (REQUIRED, NULLABLE, REPEATED), and nested structures (RECORD type with subcolumns). The displayName and description properties make entries discoverable through search. The linkedResource property connects the catalog entry to the actual resource location. This extends basic registration with structure documentation.

Beyond these examples

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

The examples reference pre-existing infrastructure such as Data Catalog entry groups and Cloud Storage buckets for fileset examples. They focus on configuring entries rather than provisioning the surrounding catalog structure.

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

  • BigQuery table integration (integratedSystem)
  • Pub/Sub topic cataloging
  • Tag attachment for flexible metadata
  • Entry group creation and management

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, Data Catalog Entry is deprecated and 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
When should I use type versus userSpecifiedType?
Use type only for entries matching the EntryType enum (currently only FILESET is allowed). For all other entries, use userSpecifiedType with a custom value like my_custom_type.
Is linkedResource required for all entries?
linkedResource is required for entries using the type property (like FILESET). For entries with userSpecifiedType, it’s optional and defaults to an empty string.
How do I create a Cloud Storage fileset entry?
Set type to FILESET and configure gcsFilesetSpec with filePatterns pointing to your GCS paths (e.g., gs://bucket/dir/*).
Immutability & Updates
What properties can't be changed after creating an entry?
The entryGroup, entryId, and type properties are immutable and cannot be modified after creation.
Naming & Validation
What are the naming rules for userSpecifiedType and userSpecifiedSystem?
Both must start with a letter or underscore, contain only letters, numbers, and underscores, are case insensitive, and must be 1-64 characters long.

Using a different cloud?

Explore analytics guides for other cloud providers: