Configure GCP Firestore Fields

The gcp:firestore/field:Field resource, part of the Pulumi GCP provider, configures single-field behavior in Firestore: indexes that optimize queries and TTL policies that trigger automatic document expiration. This guide focuses on three capabilities: index configuration for queries, TTL-based document expiration, and wildcard field configuration.

Field configuration requires an existing Firestore database and applies to collection groups, which represent all collections in the database with the same ID. The examples are intentionally small. Combine them with your own database and collection structure.

Configure indexes for query optimization

Applications that query Firestore collections need indexes to support efficient filtering and sorting.

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

const database = new gcp.firestore.Database("database", {
    project: "my-project-name",
    name: "database-id",
    locationId: "nam5",
    type: "FIRESTORE_NATIVE",
    deleteProtectionState: "DELETE_PROTECTION_ENABLED",
    deletionPolicy: "DELETE",
});
const basic = new gcp.firestore.Field("basic", {
    project: "my-project-name",
    database: database.name,
    collection: "chatrooms__35305",
    field: "basic",
    indexConfig: {
        indexes: [
            {
                order: "ASCENDING",
                queryScope: "COLLECTION_GROUP",
            },
            {
                arrayConfig: "CONTAINS",
            },
        ],
    },
});
import pulumi
import pulumi_gcp as gcp

database = gcp.firestore.Database("database",
    project="my-project-name",
    name="database-id",
    location_id="nam5",
    type="FIRESTORE_NATIVE",
    delete_protection_state="DELETE_PROTECTION_ENABLED",
    deletion_policy="DELETE")
basic = gcp.firestore.Field("basic",
    project="my-project-name",
    database=database.name,
    collection="chatrooms__35305",
    field="basic",
    index_config={
        "indexes": [
            {
                "order": "ASCENDING",
                "query_scope": "COLLECTION_GROUP",
            },
            {
                "array_config": "CONTAINS",
            },
        ],
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		database, err := firestore.NewDatabase(ctx, "database", &firestore.DatabaseArgs{
			Project:               pulumi.String("my-project-name"),
			Name:                  pulumi.String("database-id"),
			LocationId:            pulumi.String("nam5"),
			Type:                  pulumi.String("FIRESTORE_NATIVE"),
			DeleteProtectionState: pulumi.String("DELETE_PROTECTION_ENABLED"),
			DeletionPolicy:        pulumi.String("DELETE"),
		})
		if err != nil {
			return err
		}
		_, err = firestore.NewField(ctx, "basic", &firestore.FieldArgs{
			Project:    pulumi.String("my-project-name"),
			Database:   database.Name,
			Collection: pulumi.String("chatrooms__35305"),
			Field:      pulumi.String("basic"),
			IndexConfig: &firestore.FieldIndexConfigArgs{
				Indexes: firestore.FieldIndexConfigIndexArray{
					&firestore.FieldIndexConfigIndexArgs{
						Order:      pulumi.String("ASCENDING"),
						QueryScope: pulumi.String("COLLECTION_GROUP"),
					},
					&firestore.FieldIndexConfigIndexArgs{
						ArrayConfig: pulumi.String("CONTAINS"),
					},
				},
			},
		})
		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 database = new Gcp.Firestore.Database("database", new()
    {
        Project = "my-project-name",
        Name = "database-id",
        LocationId = "nam5",
        Type = "FIRESTORE_NATIVE",
        DeleteProtectionState = "DELETE_PROTECTION_ENABLED",
        DeletionPolicy = "DELETE",
    });

    var basic = new Gcp.Firestore.Field("basic", new()
    {
        Project = "my-project-name",
        Database = database.Name,
        Collection = "chatrooms__35305",
        FieldId = "basic",
        IndexConfig = new Gcp.Firestore.Inputs.FieldIndexConfigArgs
        {
            Indexes = new[]
            {
                new Gcp.Firestore.Inputs.FieldIndexConfigIndexArgs
                {
                    Order = "ASCENDING",
                    QueryScope = "COLLECTION_GROUP",
                },
                new Gcp.Firestore.Inputs.FieldIndexConfigIndexArgs
                {
                    ArrayConfig = "CONTAINS",
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.firestore.Database;
import com.pulumi.gcp.firestore.DatabaseArgs;
import com.pulumi.gcp.firestore.Field;
import com.pulumi.gcp.firestore.FieldArgs;
import com.pulumi.gcp.firestore.inputs.FieldIndexConfigArgs;
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 database = new Database("database", DatabaseArgs.builder()
            .project("my-project-name")
            .name("database-id")
            .locationId("nam5")
            .type("FIRESTORE_NATIVE")
            .deleteProtectionState("DELETE_PROTECTION_ENABLED")
            .deletionPolicy("DELETE")
            .build());

        var basic = new Field("basic", FieldArgs.builder()
            .project("my-project-name")
            .database(database.name())
            .collection("chatrooms__35305")
            .field("basic")
            .indexConfig(FieldIndexConfigArgs.builder()
                .indexes(                
                    FieldIndexConfigIndexArgs.builder()
                        .order("ASCENDING")
                        .queryScope("COLLECTION_GROUP")
                        .build(),
                    FieldIndexConfigIndexArgs.builder()
                        .arrayConfig("CONTAINS")
                        .build())
                .build())
            .build());

    }
}
resources:
  database:
    type: gcp:firestore:Database
    properties:
      project: my-project-name
      name: database-id
      locationId: nam5
      type: FIRESTORE_NATIVE
      deleteProtectionState: DELETE_PROTECTION_ENABLED
      deletionPolicy: DELETE
  basic:
    type: gcp:firestore:Field
    properties:
      project: my-project-name
      database: ${database.name}
      collection: chatrooms__35305
      field: basic
      indexConfig:
        indexes:
          - order: ASCENDING
            queryScope: COLLECTION_GROUP
          - arrayConfig: CONTAINS

The indexConfig property defines which query patterns this field supports. Each entry in the indexes array specifies either an order (ASCENDING or DESCENDING) or an arrayConfig (CONTAINS). The queryScope determines whether the index applies to a single collection or across all collections in the group (COLLECTION_GROUP).

Enable automatic document expiration with TTL

Documents that should expire after a certain time can use TTL configuration to trigger automatic deletion based on a timestamp field.

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

const database = new gcp.firestore.Database("database", {
    project: "my-project-name",
    name: "database-id",
    locationId: "nam5",
    type: "FIRESTORE_NATIVE",
    deleteProtectionState: "DELETE_PROTECTION_ENABLED",
    deletionPolicy: "DELETE",
});
const timestamp = new gcp.firestore.Field("timestamp", {
    project: "my-project-name",
    database: database.name,
    collection: "chatrooms",
    field: "timestamp",
    ttlConfig: {},
    indexConfig: {},
});
import pulumi
import pulumi_gcp as gcp

database = gcp.firestore.Database("database",
    project="my-project-name",
    name="database-id",
    location_id="nam5",
    type="FIRESTORE_NATIVE",
    delete_protection_state="DELETE_PROTECTION_ENABLED",
    deletion_policy="DELETE")
timestamp = gcp.firestore.Field("timestamp",
    project="my-project-name",
    database=database.name,
    collection="chatrooms",
    field="timestamp",
    ttl_config={},
    index_config={})
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		database, err := firestore.NewDatabase(ctx, "database", &firestore.DatabaseArgs{
			Project:               pulumi.String("my-project-name"),
			Name:                  pulumi.String("database-id"),
			LocationId:            pulumi.String("nam5"),
			Type:                  pulumi.String("FIRESTORE_NATIVE"),
			DeleteProtectionState: pulumi.String("DELETE_PROTECTION_ENABLED"),
			DeletionPolicy:        pulumi.String("DELETE"),
		})
		if err != nil {
			return err
		}
		_, err = firestore.NewField(ctx, "timestamp", &firestore.FieldArgs{
			Project:     pulumi.String("my-project-name"),
			Database:    database.Name,
			Collection:  pulumi.String("chatrooms"),
			Field:       pulumi.String("timestamp"),
			TtlConfig:   &firestore.FieldTtlConfigArgs{},
			IndexConfig: &firestore.FieldIndexConfigArgs{},
		})
		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 database = new Gcp.Firestore.Database("database", new()
    {
        Project = "my-project-name",
        Name = "database-id",
        LocationId = "nam5",
        Type = "FIRESTORE_NATIVE",
        DeleteProtectionState = "DELETE_PROTECTION_ENABLED",
        DeletionPolicy = "DELETE",
    });

    var timestamp = new Gcp.Firestore.Field("timestamp", new()
    {
        Project = "my-project-name",
        Database = database.Name,
        Collection = "chatrooms",
        FieldId = "timestamp",
        TtlConfig = null,
        IndexConfig = null,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.firestore.Database;
import com.pulumi.gcp.firestore.DatabaseArgs;
import com.pulumi.gcp.firestore.Field;
import com.pulumi.gcp.firestore.FieldArgs;
import com.pulumi.gcp.firestore.inputs.FieldTtlConfigArgs;
import com.pulumi.gcp.firestore.inputs.FieldIndexConfigArgs;
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 database = new Database("database", DatabaseArgs.builder()
            .project("my-project-name")
            .name("database-id")
            .locationId("nam5")
            .type("FIRESTORE_NATIVE")
            .deleteProtectionState("DELETE_PROTECTION_ENABLED")
            .deletionPolicy("DELETE")
            .build());

        var timestamp = new Field("timestamp", FieldArgs.builder()
            .project("my-project-name")
            .database(database.name())
            .collection("chatrooms")
            .field("timestamp")
            .ttlConfig(FieldTtlConfigArgs.builder()
                .build())
            .indexConfig(FieldIndexConfigArgs.builder()
                .build())
            .build());

    }
}
resources:
  database:
    type: gcp:firestore:Database
    properties:
      project: my-project-name
      name: database-id
      locationId: nam5
      type: FIRESTORE_NATIVE
      deleteProtectionState: DELETE_PROTECTION_ENABLED
      deletionPolicy: DELETE
  timestamp:
    type: gcp:firestore:Field
    properties:
      project: my-project-name
      database: ${database.name}
      collection: chatrooms
      field: timestamp
      ttlConfig: {}
      indexConfig: {}

Setting ttlConfig to an empty block enables TTL on the field. Firestore automatically deletes documents when the timestamp in this field passes. The indexConfig is set to an empty block here, which disables all indexes on the field.

Override inherited index configuration

When a field inherits index configuration from an ancestor but needs different settings, you can create an override.

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

const database = new gcp.firestore.Database("database", {
    project: "my-project-name",
    name: "database-id",
    locationId: "nam5",
    type: "FIRESTORE_NATIVE",
    deleteProtectionState: "DELETE_PROTECTION_ENABLED",
    deletionPolicy: "DELETE",
});
const matchOverride = new gcp.firestore.Field("match_override", {
    project: "my-project-name",
    database: database.name,
    collection: "chatrooms__62793",
    field: "field_with_same_configuration_as_ancestor",
    indexConfig: {
        indexes: [
            {
                order: "ASCENDING",
            },
            {
                order: "DESCENDING",
            },
            {
                arrayConfig: "CONTAINS",
            },
        ],
    },
});
import pulumi
import pulumi_gcp as gcp

database = gcp.firestore.Database("database",
    project="my-project-name",
    name="database-id",
    location_id="nam5",
    type="FIRESTORE_NATIVE",
    delete_protection_state="DELETE_PROTECTION_ENABLED",
    deletion_policy="DELETE")
match_override = gcp.firestore.Field("match_override",
    project="my-project-name",
    database=database.name,
    collection="chatrooms__62793",
    field="field_with_same_configuration_as_ancestor",
    index_config={
        "indexes": [
            {
                "order": "ASCENDING",
            },
            {
                "order": "DESCENDING",
            },
            {
                "array_config": "CONTAINS",
            },
        ],
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		database, err := firestore.NewDatabase(ctx, "database", &firestore.DatabaseArgs{
			Project:               pulumi.String("my-project-name"),
			Name:                  pulumi.String("database-id"),
			LocationId:            pulumi.String("nam5"),
			Type:                  pulumi.String("FIRESTORE_NATIVE"),
			DeleteProtectionState: pulumi.String("DELETE_PROTECTION_ENABLED"),
			DeletionPolicy:        pulumi.String("DELETE"),
		})
		if err != nil {
			return err
		}
		_, err = firestore.NewField(ctx, "match_override", &firestore.FieldArgs{
			Project:    pulumi.String("my-project-name"),
			Database:   database.Name,
			Collection: pulumi.String("chatrooms__62793"),
			Field:      pulumi.String("field_with_same_configuration_as_ancestor"),
			IndexConfig: &firestore.FieldIndexConfigArgs{
				Indexes: firestore.FieldIndexConfigIndexArray{
					&firestore.FieldIndexConfigIndexArgs{
						Order: pulumi.String("ASCENDING"),
					},
					&firestore.FieldIndexConfigIndexArgs{
						Order: pulumi.String("DESCENDING"),
					},
					&firestore.FieldIndexConfigIndexArgs{
						ArrayConfig: pulumi.String("CONTAINS"),
					},
				},
			},
		})
		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 database = new Gcp.Firestore.Database("database", new()
    {
        Project = "my-project-name",
        Name = "database-id",
        LocationId = "nam5",
        Type = "FIRESTORE_NATIVE",
        DeleteProtectionState = "DELETE_PROTECTION_ENABLED",
        DeletionPolicy = "DELETE",
    });

    var matchOverride = new Gcp.Firestore.Field("match_override", new()
    {
        Project = "my-project-name",
        Database = database.Name,
        Collection = "chatrooms__62793",
        FieldId = "field_with_same_configuration_as_ancestor",
        IndexConfig = new Gcp.Firestore.Inputs.FieldIndexConfigArgs
        {
            Indexes = new[]
            {
                new Gcp.Firestore.Inputs.FieldIndexConfigIndexArgs
                {
                    Order = "ASCENDING",
                },
                new Gcp.Firestore.Inputs.FieldIndexConfigIndexArgs
                {
                    Order = "DESCENDING",
                },
                new Gcp.Firestore.Inputs.FieldIndexConfigIndexArgs
                {
                    ArrayConfig = "CONTAINS",
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.firestore.Database;
import com.pulumi.gcp.firestore.DatabaseArgs;
import com.pulumi.gcp.firestore.Field;
import com.pulumi.gcp.firestore.FieldArgs;
import com.pulumi.gcp.firestore.inputs.FieldIndexConfigArgs;
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 database = new Database("database", DatabaseArgs.builder()
            .project("my-project-name")
            .name("database-id")
            .locationId("nam5")
            .type("FIRESTORE_NATIVE")
            .deleteProtectionState("DELETE_PROTECTION_ENABLED")
            .deletionPolicy("DELETE")
            .build());

        var matchOverride = new Field("matchOverride", FieldArgs.builder()
            .project("my-project-name")
            .database(database.name())
            .collection("chatrooms__62793")
            .field("field_with_same_configuration_as_ancestor")
            .indexConfig(FieldIndexConfigArgs.builder()
                .indexes(                
                    FieldIndexConfigIndexArgs.builder()
                        .order("ASCENDING")
                        .build(),
                    FieldIndexConfigIndexArgs.builder()
                        .order("DESCENDING")
                        .build(),
                    FieldIndexConfigIndexArgs.builder()
                        .arrayConfig("CONTAINS")
                        .build())
                .build())
            .build());

    }
}
resources:
  database:
    type: gcp:firestore:Database
    properties:
      project: my-project-name
      name: database-id
      locationId: nam5
      type: FIRESTORE_NATIVE
      deleteProtectionState: DELETE_PROTECTION_ENABLED
      deletionPolicy: DELETE
  matchOverride:
    type: gcp:firestore:Field
    name: match_override
    properties:
      project: my-project-name
      database: ${database.name}
      collection: chatrooms__62793
      field: field_with_same_configuration_as_ancestor
      indexConfig:
        indexes:
          - order: ASCENDING
          - order: DESCENDING
          - arrayConfig: CONTAINS

This configuration replaces any inherited indexes with the specified set. The indexes array includes both ascending and descending order indexes plus an array-contains index. Without queryScope specified, indexes default to single-collection scope.

Apply index configuration to all fields

Collection groups sometimes need consistent index configuration across all fields without specifying each individually.

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

const database = new gcp.firestore.Database("database", {
    project: "my-project-name",
    name: "database-id",
    locationId: "nam5",
    type: "FIRESTORE_NATIVE",
    deleteProtectionState: "DELETE_PROTECTION_ENABLED",
    deletionPolicy: "DELETE",
});
const wildcard = new gcp.firestore.Field("wildcard", {
    project: "my-project-name",
    database: database.name,
    collection: "chatrooms__55438",
    field: "*",
    indexConfig: {
        indexes: [
            {
                order: "ASCENDING",
                queryScope: "COLLECTION_GROUP",
            },
            {
                arrayConfig: "CONTAINS",
            },
        ],
    },
});
import pulumi
import pulumi_gcp as gcp

database = gcp.firestore.Database("database",
    project="my-project-name",
    name="database-id",
    location_id="nam5",
    type="FIRESTORE_NATIVE",
    delete_protection_state="DELETE_PROTECTION_ENABLED",
    deletion_policy="DELETE")
wildcard = gcp.firestore.Field("wildcard",
    project="my-project-name",
    database=database.name,
    collection="chatrooms__55438",
    field="*",
    index_config={
        "indexes": [
            {
                "order": "ASCENDING",
                "query_scope": "COLLECTION_GROUP",
            },
            {
                "array_config": "CONTAINS",
            },
        ],
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		database, err := firestore.NewDatabase(ctx, "database", &firestore.DatabaseArgs{
			Project:               pulumi.String("my-project-name"),
			Name:                  pulumi.String("database-id"),
			LocationId:            pulumi.String("nam5"),
			Type:                  pulumi.String("FIRESTORE_NATIVE"),
			DeleteProtectionState: pulumi.String("DELETE_PROTECTION_ENABLED"),
			DeletionPolicy:        pulumi.String("DELETE"),
		})
		if err != nil {
			return err
		}
		_, err = firestore.NewField(ctx, "wildcard", &firestore.FieldArgs{
			Project:    pulumi.String("my-project-name"),
			Database:   database.Name,
			Collection: pulumi.String("chatrooms__55438"),
			Field:      pulumi.String("*"),
			IndexConfig: &firestore.FieldIndexConfigArgs{
				Indexes: firestore.FieldIndexConfigIndexArray{
					&firestore.FieldIndexConfigIndexArgs{
						Order:      pulumi.String("ASCENDING"),
						QueryScope: pulumi.String("COLLECTION_GROUP"),
					},
					&firestore.FieldIndexConfigIndexArgs{
						ArrayConfig: pulumi.String("CONTAINS"),
					},
				},
			},
		})
		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 database = new Gcp.Firestore.Database("database", new()
    {
        Project = "my-project-name",
        Name = "database-id",
        LocationId = "nam5",
        Type = "FIRESTORE_NATIVE",
        DeleteProtectionState = "DELETE_PROTECTION_ENABLED",
        DeletionPolicy = "DELETE",
    });

    var wildcard = new Gcp.Firestore.Field("wildcard", new()
    {
        Project = "my-project-name",
        Database = database.Name,
        Collection = "chatrooms__55438",
        FieldId = "*",
        IndexConfig = new Gcp.Firestore.Inputs.FieldIndexConfigArgs
        {
            Indexes = new[]
            {
                new Gcp.Firestore.Inputs.FieldIndexConfigIndexArgs
                {
                    Order = "ASCENDING",
                    QueryScope = "COLLECTION_GROUP",
                },
                new Gcp.Firestore.Inputs.FieldIndexConfigIndexArgs
                {
                    ArrayConfig = "CONTAINS",
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.firestore.Database;
import com.pulumi.gcp.firestore.DatabaseArgs;
import com.pulumi.gcp.firestore.Field;
import com.pulumi.gcp.firestore.FieldArgs;
import com.pulumi.gcp.firestore.inputs.FieldIndexConfigArgs;
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 database = new Database("database", DatabaseArgs.builder()
            .project("my-project-name")
            .name("database-id")
            .locationId("nam5")
            .type("FIRESTORE_NATIVE")
            .deleteProtectionState("DELETE_PROTECTION_ENABLED")
            .deletionPolicy("DELETE")
            .build());

        var wildcard = new Field("wildcard", FieldArgs.builder()
            .project("my-project-name")
            .database(database.name())
            .collection("chatrooms__55438")
            .field("*")
            .indexConfig(FieldIndexConfigArgs.builder()
                .indexes(                
                    FieldIndexConfigIndexArgs.builder()
                        .order("ASCENDING")
                        .queryScope("COLLECTION_GROUP")
                        .build(),
                    FieldIndexConfigIndexArgs.builder()
                        .arrayConfig("CONTAINS")
                        .build())
                .build())
            .build());

    }
}
resources:
  database:
    type: gcp:firestore:Database
    properties:
      project: my-project-name
      name: database-id
      locationId: nam5
      type: FIRESTORE_NATIVE
      deleteProtectionState: DELETE_PROTECTION_ENABLED
      deletionPolicy: DELETE
  wildcard:
    type: gcp:firestore:Field
    properties:
      project: my-project-name
      database: ${database.name}
      collection: chatrooms__55438
      field: '*'
      indexConfig:
        indexes:
          - order: ASCENDING
            queryScope: COLLECTION_GROUP
          - arrayConfig: CONTAINS

Setting the field property to “*” applies the configuration to all fields in the collection group. This wildcard approach ensures consistent query support across the entire collection without creating individual field resources.

Beyond these examples

These snippets focus on specific field-level features: index configuration for queries, TTL-based document expiration, and wildcard field configuration. They’re intentionally minimal rather than complete Firestore deployments.

The examples rely on pre-existing infrastructure such as a Firestore database (created via gcp.firestore.Database) and collection groups with documents. They focus on field configuration rather than database or collection provisioning.

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

  • Index exemptions and disabling all indexes
  • Composite indexes spanning multiple fields
  • Index state monitoring and management
  • Field-level security rules

These omissions are intentional: the goal is to illustrate how each field feature is wired, not provide drop-in Firestore modules. See the Firestore Field resource reference for all available configuration options.

Let's configure GCP Firestore Fields

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Prerequisites & Setup
Do I need to create a Firestore database before creating field overrides?
Yes, this resource requires an existing Firestore database. Create a gcp.firestore.Database resource with locationId set to your chosen location before creating field overrides.
Field Configuration
What properties can't I change after creating a field override?
The collection, field, project, and database properties are immutable. Changing any of these forces resource recreation, so plan carefully during initial setup.
What does setting field to '*' do?
The wildcard * applies the index configuration to all fields in the collection group, not just a single field.
What's the default database if I don't specify one?
The database property defaults to "(default)" if not specified.
Index Configuration
How do I disable all indexes on a field?
Set indexConfig to an empty block (indexConfig: {}). This overrides any inherited configuration and disables all indexes on the field.
How do I configure custom indexes for a field?
Use indexConfig with an indexes array. Each index can specify order (ASCENDING or DESCENDING) with optional queryScope, or arrayConfig (CONTAINS) for array fields.
TTL Configuration
How do I enable or disable TTL on a field?
To enable TTL, set ttlConfig to an empty block (ttlConfig: {}). To disable TTL, leave ttlConfig unset entirely. Note that this behavior differs from indexConfig.

Using a different cloud?

Explore database guides for other cloud providers: