Create GCP Healthcare FHIR Stores

The gcp:healthcare/fhirStore:FhirStore resource, part of the Pulumi GCP provider, defines a FHIR store within a Healthcare dataset: its FHIR version, data integrity rules, and export destinations. This guide focuses on three capabilities: FHIR version and integrity configuration, BigQuery streaming and Pub/Sub notifications, and consent enforcement and validation controls.

FHIR stores belong to Healthcare datasets and reference Pub/Sub topics or BigQuery datasets for notifications and streaming. The examples are intentionally small. Combine them with your own Healthcare datasets, notification infrastructure, and access policies.

Create a FHIR store with Pub/Sub notifications

Most deployments start by creating a store within a Healthcare dataset, choosing a FHIR version, and configuring integrity behavior. Pub/Sub notifications let downstream systems react to resource changes.

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

const topic = new gcp.pubsub.Topic("topic", {name: "fhir-notifications"});
const dataset = new gcp.healthcare.Dataset("dataset", {
    name: "example-dataset",
    location: "us-central1",
});
const _default = new gcp.healthcare.FhirStore("default", {
    name: "example-fhir-store",
    dataset: dataset.id,
    version: "R4",
    complexDataTypeReferenceParsing: "DISABLED",
    enableUpdateCreate: false,
    disableReferentialIntegrity: false,
    disableResourceVersioning: false,
    enableHistoryImport: false,
    defaultSearchHandlingStrict: false,
    notificationConfigs: [{
        pubsubTopic: topic.id,
    }],
    labels: {
        label1: "labelvalue1",
    },
});
import pulumi
import pulumi_gcp as gcp

topic = gcp.pubsub.Topic("topic", name="fhir-notifications")
dataset = gcp.healthcare.Dataset("dataset",
    name="example-dataset",
    location="us-central1")
default = gcp.healthcare.FhirStore("default",
    name="example-fhir-store",
    dataset=dataset.id,
    version="R4",
    complex_data_type_reference_parsing="DISABLED",
    enable_update_create=False,
    disable_referential_integrity=False,
    disable_resource_versioning=False,
    enable_history_import=False,
    default_search_handling_strict=False,
    notification_configs=[{
        "pubsub_topic": topic.id,
    }],
    labels={
        "label1": "labelvalue1",
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/healthcare"
	"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 {
		topic, err := pubsub.NewTopic(ctx, "topic", &pubsub.TopicArgs{
			Name: pulumi.String("fhir-notifications"),
		})
		if err != nil {
			return err
		}
		dataset, err := healthcare.NewDataset(ctx, "dataset", &healthcare.DatasetArgs{
			Name:     pulumi.String("example-dataset"),
			Location: pulumi.String("us-central1"),
		})
		if err != nil {
			return err
		}
		_, err = healthcare.NewFhirStore(ctx, "default", &healthcare.FhirStoreArgs{
			Name:                            pulumi.String("example-fhir-store"),
			Dataset:                         dataset.ID(),
			Version:                         pulumi.String("R4"),
			ComplexDataTypeReferenceParsing: pulumi.String("DISABLED"),
			EnableUpdateCreate:              pulumi.Bool(false),
			DisableReferentialIntegrity:     pulumi.Bool(false),
			DisableResourceVersioning:       pulumi.Bool(false),
			EnableHistoryImport:             pulumi.Bool(false),
			DefaultSearchHandlingStrict:     pulumi.Bool(false),
			NotificationConfigs: healthcare.FhirStoreNotificationConfigArray{
				&healthcare.FhirStoreNotificationConfigArgs{
					PubsubTopic: topic.ID(),
				},
			},
			Labels: pulumi.StringMap{
				"label1": pulumi.String("labelvalue1"),
			},
		})
		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 topic = new Gcp.PubSub.Topic("topic", new()
    {
        Name = "fhir-notifications",
    });

    var dataset = new Gcp.Healthcare.Dataset("dataset", new()
    {
        Name = "example-dataset",
        Location = "us-central1",
    });

    var @default = new Gcp.Healthcare.FhirStore("default", new()
    {
        Name = "example-fhir-store",
        Dataset = dataset.Id,
        Version = "R4",
        ComplexDataTypeReferenceParsing = "DISABLED",
        EnableUpdateCreate = false,
        DisableReferentialIntegrity = false,
        DisableResourceVersioning = false,
        EnableHistoryImport = false,
        DefaultSearchHandlingStrict = false,
        NotificationConfigs = new[]
        {
            new Gcp.Healthcare.Inputs.FhirStoreNotificationConfigArgs
            {
                PubsubTopic = topic.Id,
            },
        },
        Labels = 
        {
            { "label1", "labelvalue1" },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.pubsub.Topic;
import com.pulumi.gcp.pubsub.TopicArgs;
import com.pulumi.gcp.healthcare.Dataset;
import com.pulumi.gcp.healthcare.DatasetArgs;
import com.pulumi.gcp.healthcare.FhirStore;
import com.pulumi.gcp.healthcare.FhirStoreArgs;
import com.pulumi.gcp.healthcare.inputs.FhirStoreNotificationConfigArgs;
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 topic = new Topic("topic", TopicArgs.builder()
            .name("fhir-notifications")
            .build());

        var dataset = new Dataset("dataset", DatasetArgs.builder()
            .name("example-dataset")
            .location("us-central1")
            .build());

        var default_ = new FhirStore("default", FhirStoreArgs.builder()
            .name("example-fhir-store")
            .dataset(dataset.id())
            .version("R4")
            .complexDataTypeReferenceParsing("DISABLED")
            .enableUpdateCreate(false)
            .disableReferentialIntegrity(false)
            .disableResourceVersioning(false)
            .enableHistoryImport(false)
            .defaultSearchHandlingStrict(false)
            .notificationConfigs(FhirStoreNotificationConfigArgs.builder()
                .pubsubTopic(topic.id())
                .build())
            .labels(Map.of("label1", "labelvalue1"))
            .build());

    }
}
resources:
  default:
    type: gcp:healthcare:FhirStore
    properties:
      name: example-fhir-store
      dataset: ${dataset.id}
      version: R4
      complexDataTypeReferenceParsing: DISABLED
      enableUpdateCreate: false
      disableReferentialIntegrity: false
      disableResourceVersioning: false
      enableHistoryImport: false
      defaultSearchHandlingStrict: false
      notificationConfigs:
        - pubsubTopic: ${topic.id}
      labels:
        label1: labelvalue1
  topic:
    type: gcp:pubsub:Topic
    properties:
      name: fhir-notifications
  dataset:
    type: gcp:healthcare:Dataset
    properties:
      name: example-dataset
      location: us-central1

The dataset property places the store within a Healthcare dataset. The version property selects the FHIR specification (DSTU2, STU3, or R4). The complexDataTypeReferenceParsing property controls whether references inside complex data types like Extensions are parsed for integrity checks. The disableReferentialIntegrity and disableResourceVersioning properties are immutable after creation; changing them recreates the store and removes all data. The notificationConfigs array sends change events to Pub/Sub topics.

Stream FHIR resources to BigQuery for analytics

Healthcare analytics pipelines often need FHIR data in BigQuery for SQL-based analysis. Streaming configs automatically export resource mutations as they occur.

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

const dataset = new gcp.healthcare.Dataset("dataset", {
    name: "example-dataset",
    location: "us-central1",
});
const bqDataset = new gcp.bigquery.Dataset("bq_dataset", {
    datasetId: "bq_example_dataset",
    friendlyName: "test",
    description: "This is a test description",
    location: "US",
    deleteContentsOnDestroy: true,
});
const _default = new gcp.healthcare.FhirStore("default", {
    name: "example-fhir-store",
    dataset: dataset.id,
    version: "R4",
    enableUpdateCreate: false,
    disableReferentialIntegrity: false,
    disableResourceVersioning: false,
    enableHistoryImport: false,
    labels: {
        label1: "labelvalue1",
    },
    streamConfigs: [{
        resourceTypes: ["Observation"],
        bigqueryDestination: {
            datasetUri: pulumi.interpolate`bq://${bqDataset.project}.${bqDataset.datasetId}`,
            schemaConfig: {
                recursiveStructureDepth: 3,
                lastUpdatedPartitionConfig: {
                    type: "HOUR",
                    expirationMs: "1000000",
                },
            },
        },
    }],
});
const topic = new gcp.pubsub.Topic("topic", {name: "fhir-notifications"});
import pulumi
import pulumi_gcp as gcp

dataset = gcp.healthcare.Dataset("dataset",
    name="example-dataset",
    location="us-central1")
bq_dataset = gcp.bigquery.Dataset("bq_dataset",
    dataset_id="bq_example_dataset",
    friendly_name="test",
    description="This is a test description",
    location="US",
    delete_contents_on_destroy=True)
default = gcp.healthcare.FhirStore("default",
    name="example-fhir-store",
    dataset=dataset.id,
    version="R4",
    enable_update_create=False,
    disable_referential_integrity=False,
    disable_resource_versioning=False,
    enable_history_import=False,
    labels={
        "label1": "labelvalue1",
    },
    stream_configs=[{
        "resource_types": ["Observation"],
        "bigquery_destination": {
            "dataset_uri": pulumi.Output.all(
                project=bq_dataset.project,
                dataset_id=bq_dataset.dataset_id
).apply(lambda resolved_outputs: f"bq://{resolved_outputs['project']}.{resolved_outputs['dataset_id']}")
,
            "schema_config": {
                "recursive_structure_depth": 3,
                "last_updated_partition_config": {
                    "type": "HOUR",
                    "expiration_ms": "1000000",
                },
            },
        },
    }])
topic = gcp.pubsub.Topic("topic", name="fhir-notifications")
package main

import (
	"fmt"

	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/bigquery"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/healthcare"
	"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 {
		dataset, err := healthcare.NewDataset(ctx, "dataset", &healthcare.DatasetArgs{
			Name:     pulumi.String("example-dataset"),
			Location: pulumi.String("us-central1"),
		})
		if err != nil {
			return err
		}
		bqDataset, err := bigquery.NewDataset(ctx, "bq_dataset", &bigquery.DatasetArgs{
			DatasetId:               pulumi.String("bq_example_dataset"),
			FriendlyName:            pulumi.String("test"),
			Description:             pulumi.String("This is a test description"),
			Location:                pulumi.String("US"),
			DeleteContentsOnDestroy: pulumi.Bool(true),
		})
		if err != nil {
			return err
		}
		_, err = healthcare.NewFhirStore(ctx, "default", &healthcare.FhirStoreArgs{
			Name:                        pulumi.String("example-fhir-store"),
			Dataset:                     dataset.ID(),
			Version:                     pulumi.String("R4"),
			EnableUpdateCreate:          pulumi.Bool(false),
			DisableReferentialIntegrity: pulumi.Bool(false),
			DisableResourceVersioning:   pulumi.Bool(false),
			EnableHistoryImport:         pulumi.Bool(false),
			Labels: pulumi.StringMap{
				"label1": pulumi.String("labelvalue1"),
			},
			StreamConfigs: healthcare.FhirStoreStreamConfigArray{
				&healthcare.FhirStoreStreamConfigArgs{
					ResourceTypes: pulumi.StringArray{
						pulumi.String("Observation"),
					},
					BigqueryDestination: &healthcare.FhirStoreStreamConfigBigqueryDestinationArgs{
						DatasetUri: pulumi.All(bqDataset.Project, bqDataset.DatasetId).ApplyT(func(_args []interface{}) (string, error) {
							project := _args[0].(string)
							datasetId := _args[1].(string)
							return fmt.Sprintf("bq://%v.%v", project, datasetId), nil
						}).(pulumi.StringOutput),
						SchemaConfig: &healthcare.FhirStoreStreamConfigBigqueryDestinationSchemaConfigArgs{
							RecursiveStructureDepth: pulumi.Int(3),
							LastUpdatedPartitionConfig: &healthcare.FhirStoreStreamConfigBigqueryDestinationSchemaConfigLastUpdatedPartitionConfigArgs{
								Type:         pulumi.String("HOUR"),
								ExpirationMs: pulumi.String("1000000"),
							},
						},
					},
				},
			},
		})
		if err != nil {
			return err
		}
		_, err = pubsub.NewTopic(ctx, "topic", &pubsub.TopicArgs{
			Name: pulumi.String("fhir-notifications"),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var dataset = new Gcp.Healthcare.Dataset("dataset", new()
    {
        Name = "example-dataset",
        Location = "us-central1",
    });

    var bqDataset = new Gcp.BigQuery.Dataset("bq_dataset", new()
    {
        DatasetId = "bq_example_dataset",
        FriendlyName = "test",
        Description = "This is a test description",
        Location = "US",
        DeleteContentsOnDestroy = true,
    });

    var @default = new Gcp.Healthcare.FhirStore("default", new()
    {
        Name = "example-fhir-store",
        Dataset = dataset.Id,
        Version = "R4",
        EnableUpdateCreate = false,
        DisableReferentialIntegrity = false,
        DisableResourceVersioning = false,
        EnableHistoryImport = false,
        Labels = 
        {
            { "label1", "labelvalue1" },
        },
        StreamConfigs = new[]
        {
            new Gcp.Healthcare.Inputs.FhirStoreStreamConfigArgs
            {
                ResourceTypes = new[]
                {
                    "Observation",
                },
                BigqueryDestination = new Gcp.Healthcare.Inputs.FhirStoreStreamConfigBigqueryDestinationArgs
                {
                    DatasetUri = Output.Tuple(bqDataset.Project, bqDataset.DatasetId).Apply(values =>
                    {
                        var project = values.Item1;
                        var datasetId = values.Item2;
                        return $"bq://{project}.{datasetId}";
                    }),
                    SchemaConfig = new Gcp.Healthcare.Inputs.FhirStoreStreamConfigBigqueryDestinationSchemaConfigArgs
                    {
                        RecursiveStructureDepth = 3,
                        LastUpdatedPartitionConfig = new Gcp.Healthcare.Inputs.FhirStoreStreamConfigBigqueryDestinationSchemaConfigLastUpdatedPartitionConfigArgs
                        {
                            Type = "HOUR",
                            ExpirationMs = "1000000",
                        },
                    },
                },
            },
        },
    });

    var topic = new Gcp.PubSub.Topic("topic", new()
    {
        Name = "fhir-notifications",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.healthcare.FhirStore;
import com.pulumi.gcp.healthcare.FhirStoreArgs;
import com.pulumi.gcp.healthcare.inputs.FhirStoreStreamConfigArgs;
import com.pulumi.gcp.healthcare.inputs.FhirStoreStreamConfigBigqueryDestinationArgs;
import com.pulumi.gcp.healthcare.inputs.FhirStoreStreamConfigBigqueryDestinationSchemaConfigArgs;
import com.pulumi.gcp.healthcare.inputs.FhirStoreStreamConfigBigqueryDestinationSchemaConfigLastUpdatedPartitionConfigArgs;
import com.pulumi.gcp.pubsub.Topic;
import com.pulumi.gcp.pubsub.TopicArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var dataset = new com.pulumi.gcp.healthcare.Dataset("dataset", com.pulumi.gcp.healthcare.DatasetArgs.builder()
            .name("example-dataset")
            .location("us-central1")
            .build());

        var bqDataset = new com.pulumi.gcp.bigquery.Dataset("bqDataset", com.pulumi.gcp.bigquery.DatasetArgs.builder()
            .datasetId("bq_example_dataset")
            .friendlyName("test")
            .description("This is a test description")
            .location("US")
            .deleteContentsOnDestroy(true)
            .build());

        var default_ = new FhirStore("default", FhirStoreArgs.builder()
            .name("example-fhir-store")
            .dataset(dataset.id())
            .version("R4")
            .enableUpdateCreate(false)
            .disableReferentialIntegrity(false)
            .disableResourceVersioning(false)
            .enableHistoryImport(false)
            .labels(Map.of("label1", "labelvalue1"))
            .streamConfigs(FhirStoreStreamConfigArgs.builder()
                .resourceTypes("Observation")
                .bigqueryDestination(FhirStoreStreamConfigBigqueryDestinationArgs.builder()
                    .datasetUri(Output.tuple(bqDataset.project(), bqDataset.datasetId()).applyValue(values -> {
                        var project = values.t1;
                        var datasetId = values.t2;
                        return String.format("bq://%s.%s", project,datasetId);
                    }))
                    .schemaConfig(FhirStoreStreamConfigBigqueryDestinationSchemaConfigArgs.builder()
                        .recursiveStructureDepth(3)
                        .lastUpdatedPartitionConfig(FhirStoreStreamConfigBigqueryDestinationSchemaConfigLastUpdatedPartitionConfigArgs.builder()
                            .type("HOUR")
                            .expirationMs("1000000")
                            .build())
                        .build())
                    .build())
                .build())
            .build());

        var topic = new Topic("topic", TopicArgs.builder()
            .name("fhir-notifications")
            .build());

    }
}
resources:
  default:
    type: gcp:healthcare:FhirStore
    properties:
      name: example-fhir-store
      dataset: ${dataset.id}
      version: R4
      enableUpdateCreate: false
      disableReferentialIntegrity: false
      disableResourceVersioning: false
      enableHistoryImport: false
      labels:
        label1: labelvalue1
      streamConfigs:
        - resourceTypes:
            - Observation
          bigqueryDestination:
            datasetUri: bq://${bqDataset.project}.${bqDataset.datasetId}
            schemaConfig:
              recursiveStructureDepth: 3
              lastUpdatedPartitionConfig:
                type: HOUR
                expirationMs: 1e+06
  topic:
    type: gcp:pubsub:Topic
    properties:
      name: fhir-notifications
  dataset:
    type: gcp:healthcare:Dataset
    properties:
      name: example-dataset
      location: us-central1
  bqDataset:
    type: gcp:bigquery:Dataset
    name: bq_dataset
    properties:
      datasetId: bq_example_dataset
      friendlyName: test
      description: This is a test description
      location: US
      deleteContentsOnDestroy: true

The streamConfigs array defines export destinations. Each config specifies resourceTypes to stream (like Observation) and a bigqueryDestination with a datasetUri pointing to your BigQuery dataset. The schemaConfig controls how FHIR resources are flattened into BigQuery tables: recursiveStructureDepth sets nesting depth, and lastUpdatedPartitionConfig creates time-based partitions. Before adding streaming configs, grant the bigquery.dataEditor role to your project’s Cloud Healthcare Service Agent.

Configure detailed Pub/Sub notifications with full resources

Some notification consumers need complete resource payloads rather than just change events.

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

const topic = new gcp.pubsub.Topic("topic", {name: "fhir-notifications"});
const dataset = new gcp.healthcare.Dataset("dataset", {
    name: "example-dataset",
    location: "us-central1",
});
const _default = new gcp.healthcare.FhirStore("default", {
    name: "example-fhir-store",
    dataset: dataset.id,
    version: "R4",
    enableUpdateCreate: false,
    disableReferentialIntegrity: false,
    disableResourceVersioning: false,
    enableHistoryImport: false,
    labels: {
        label1: "labelvalue1",
    },
    notificationConfigs: [{
        pubsubTopic: topic.id,
        sendFullResource: true,
        sendPreviousResourceOnDelete: true,
    }],
});
import pulumi
import pulumi_gcp as gcp

topic = gcp.pubsub.Topic("topic", name="fhir-notifications")
dataset = gcp.healthcare.Dataset("dataset",
    name="example-dataset",
    location="us-central1")
default = gcp.healthcare.FhirStore("default",
    name="example-fhir-store",
    dataset=dataset.id,
    version="R4",
    enable_update_create=False,
    disable_referential_integrity=False,
    disable_resource_versioning=False,
    enable_history_import=False,
    labels={
        "label1": "labelvalue1",
    },
    notification_configs=[{
        "pubsub_topic": topic.id,
        "send_full_resource": True,
        "send_previous_resource_on_delete": True,
    }])
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/healthcare"
	"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 {
		topic, err := pubsub.NewTopic(ctx, "topic", &pubsub.TopicArgs{
			Name: pulumi.String("fhir-notifications"),
		})
		if err != nil {
			return err
		}
		dataset, err := healthcare.NewDataset(ctx, "dataset", &healthcare.DatasetArgs{
			Name:     pulumi.String("example-dataset"),
			Location: pulumi.String("us-central1"),
		})
		if err != nil {
			return err
		}
		_, err = healthcare.NewFhirStore(ctx, "default", &healthcare.FhirStoreArgs{
			Name:                        pulumi.String("example-fhir-store"),
			Dataset:                     dataset.ID(),
			Version:                     pulumi.String("R4"),
			EnableUpdateCreate:          pulumi.Bool(false),
			DisableReferentialIntegrity: pulumi.Bool(false),
			DisableResourceVersioning:   pulumi.Bool(false),
			EnableHistoryImport:         pulumi.Bool(false),
			Labels: pulumi.StringMap{
				"label1": pulumi.String("labelvalue1"),
			},
			NotificationConfigs: healthcare.FhirStoreNotificationConfigArray{
				&healthcare.FhirStoreNotificationConfigArgs{
					PubsubTopic:                  topic.ID(),
					SendFullResource:             pulumi.Bool(true),
					SendPreviousResourceOnDelete: 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 topic = new Gcp.PubSub.Topic("topic", new()
    {
        Name = "fhir-notifications",
    });

    var dataset = new Gcp.Healthcare.Dataset("dataset", new()
    {
        Name = "example-dataset",
        Location = "us-central1",
    });

    var @default = new Gcp.Healthcare.FhirStore("default", new()
    {
        Name = "example-fhir-store",
        Dataset = dataset.Id,
        Version = "R4",
        EnableUpdateCreate = false,
        DisableReferentialIntegrity = false,
        DisableResourceVersioning = false,
        EnableHistoryImport = false,
        Labels = 
        {
            { "label1", "labelvalue1" },
        },
        NotificationConfigs = new[]
        {
            new Gcp.Healthcare.Inputs.FhirStoreNotificationConfigArgs
            {
                PubsubTopic = topic.Id,
                SendFullResource = true,
                SendPreviousResourceOnDelete = true,
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.pubsub.Topic;
import com.pulumi.gcp.pubsub.TopicArgs;
import com.pulumi.gcp.healthcare.Dataset;
import com.pulumi.gcp.healthcare.DatasetArgs;
import com.pulumi.gcp.healthcare.FhirStore;
import com.pulumi.gcp.healthcare.FhirStoreArgs;
import com.pulumi.gcp.healthcare.inputs.FhirStoreNotificationConfigArgs;
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 topic = new Topic("topic", TopicArgs.builder()
            .name("fhir-notifications")
            .build());

        var dataset = new Dataset("dataset", DatasetArgs.builder()
            .name("example-dataset")
            .location("us-central1")
            .build());

        var default_ = new FhirStore("default", FhirStoreArgs.builder()
            .name("example-fhir-store")
            .dataset(dataset.id())
            .version("R4")
            .enableUpdateCreate(false)
            .disableReferentialIntegrity(false)
            .disableResourceVersioning(false)
            .enableHistoryImport(false)
            .labels(Map.of("label1", "labelvalue1"))
            .notificationConfigs(FhirStoreNotificationConfigArgs.builder()
                .pubsubTopic(topic.id())
                .sendFullResource(true)
                .sendPreviousResourceOnDelete(true)
                .build())
            .build());

    }
}
resources:
  default:
    type: gcp:healthcare:FhirStore
    properties:
      name: example-fhir-store
      dataset: ${dataset.id}
      version: R4
      enableUpdateCreate: false
      disableReferentialIntegrity: false
      disableResourceVersioning: false
      enableHistoryImport: false
      labels:
        label1: labelvalue1
      notificationConfigs:
        - pubsubTopic: ${topic.id}
          sendFullResource: true
          sendPreviousResourceOnDelete: true
  topic:
    type: gcp:pubsub:Topic
    properties:
      name: fhir-notifications
  dataset:
    type: gcp:healthcare:Dataset
    properties:
      name: example-dataset
      location: us-central1

The sendFullResource property includes the entire FHIR resource in notifications, not just metadata. The sendPreviousResourceOnDelete property sends the resource state before deletion, allowing consumers to track what was removed. This extends the basic notification setup with additional payload options.

Healthcare applications subject to privacy regulations often need to enforce patient consent before allowing resource access.

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

const topic = new gcp.pubsub.Topic("topic", {name: "fhir-notifications"});
const dataset = new gcp.healthcare.Dataset("dataset", {
    name: "example-dataset",
    location: "us-central1",
});
const _default = new gcp.healthcare.FhirStore("default", {
    name: "example-fhir-store",
    dataset: dataset.id,
    version: "R4",
    complexDataTypeReferenceParsing: "DISABLED",
    enableUpdateCreate: false,
    disableReferentialIntegrity: false,
    disableResourceVersioning: false,
    enableHistoryImport: false,
    defaultSearchHandlingStrict: false,
    notificationConfigs: [{
        pubsubTopic: topic.id,
    }],
    labels: {
        label1: "labelvalue1",
    },
    consentConfig: {
        version: "V1",
        accessEnforced: true,
        consentHeaderHandling: {
            profile: "REQUIRED_ON_READ",
        },
        accessDeterminationLogConfig: {
            logLevel: "VERBOSE",
        },
    },
});
import pulumi
import pulumi_gcp as gcp

topic = gcp.pubsub.Topic("topic", name="fhir-notifications")
dataset = gcp.healthcare.Dataset("dataset",
    name="example-dataset",
    location="us-central1")
default = gcp.healthcare.FhirStore("default",
    name="example-fhir-store",
    dataset=dataset.id,
    version="R4",
    complex_data_type_reference_parsing="DISABLED",
    enable_update_create=False,
    disable_referential_integrity=False,
    disable_resource_versioning=False,
    enable_history_import=False,
    default_search_handling_strict=False,
    notification_configs=[{
        "pubsub_topic": topic.id,
    }],
    labels={
        "label1": "labelvalue1",
    },
    consent_config={
        "version": "V1",
        "access_enforced": True,
        "consent_header_handling": {
            "profile": "REQUIRED_ON_READ",
        },
        "access_determination_log_config": {
            "log_level": "VERBOSE",
        },
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/healthcare"
	"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 {
		topic, err := pubsub.NewTopic(ctx, "topic", &pubsub.TopicArgs{
			Name: pulumi.String("fhir-notifications"),
		})
		if err != nil {
			return err
		}
		dataset, err := healthcare.NewDataset(ctx, "dataset", &healthcare.DatasetArgs{
			Name:     pulumi.String("example-dataset"),
			Location: pulumi.String("us-central1"),
		})
		if err != nil {
			return err
		}
		_, err = healthcare.NewFhirStore(ctx, "default", &healthcare.FhirStoreArgs{
			Name:                            pulumi.String("example-fhir-store"),
			Dataset:                         dataset.ID(),
			Version:                         pulumi.String("R4"),
			ComplexDataTypeReferenceParsing: pulumi.String("DISABLED"),
			EnableUpdateCreate:              pulumi.Bool(false),
			DisableReferentialIntegrity:     pulumi.Bool(false),
			DisableResourceVersioning:       pulumi.Bool(false),
			EnableHistoryImport:             pulumi.Bool(false),
			DefaultSearchHandlingStrict:     pulumi.Bool(false),
			NotificationConfigs: healthcare.FhirStoreNotificationConfigArray{
				&healthcare.FhirStoreNotificationConfigArgs{
					PubsubTopic: topic.ID(),
				},
			},
			Labels: pulumi.StringMap{
				"label1": pulumi.String("labelvalue1"),
			},
			ConsentConfig: &healthcare.FhirStoreConsentConfigArgs{
				Version:        pulumi.String("V1"),
				AccessEnforced: pulumi.Bool(true),
				ConsentHeaderHandling: &healthcare.FhirStoreConsentConfigConsentHeaderHandlingArgs{
					Profile: pulumi.String("REQUIRED_ON_READ"),
				},
				AccessDeterminationLogConfig: &healthcare.FhirStoreConsentConfigAccessDeterminationLogConfigArgs{
					LogLevel: pulumi.String("VERBOSE"),
				},
			},
		})
		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 topic = new Gcp.PubSub.Topic("topic", new()
    {
        Name = "fhir-notifications",
    });

    var dataset = new Gcp.Healthcare.Dataset("dataset", new()
    {
        Name = "example-dataset",
        Location = "us-central1",
    });

    var @default = new Gcp.Healthcare.FhirStore("default", new()
    {
        Name = "example-fhir-store",
        Dataset = dataset.Id,
        Version = "R4",
        ComplexDataTypeReferenceParsing = "DISABLED",
        EnableUpdateCreate = false,
        DisableReferentialIntegrity = false,
        DisableResourceVersioning = false,
        EnableHistoryImport = false,
        DefaultSearchHandlingStrict = false,
        NotificationConfigs = new[]
        {
            new Gcp.Healthcare.Inputs.FhirStoreNotificationConfigArgs
            {
                PubsubTopic = topic.Id,
            },
        },
        Labels = 
        {
            { "label1", "labelvalue1" },
        },
        ConsentConfig = new Gcp.Healthcare.Inputs.FhirStoreConsentConfigArgs
        {
            Version = "V1",
            AccessEnforced = true,
            ConsentHeaderHandling = new Gcp.Healthcare.Inputs.FhirStoreConsentConfigConsentHeaderHandlingArgs
            {
                Profile = "REQUIRED_ON_READ",
            },
            AccessDeterminationLogConfig = new Gcp.Healthcare.Inputs.FhirStoreConsentConfigAccessDeterminationLogConfigArgs
            {
                LogLevel = "VERBOSE",
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.pubsub.Topic;
import com.pulumi.gcp.pubsub.TopicArgs;
import com.pulumi.gcp.healthcare.Dataset;
import com.pulumi.gcp.healthcare.DatasetArgs;
import com.pulumi.gcp.healthcare.FhirStore;
import com.pulumi.gcp.healthcare.FhirStoreArgs;
import com.pulumi.gcp.healthcare.inputs.FhirStoreNotificationConfigArgs;
import com.pulumi.gcp.healthcare.inputs.FhirStoreConsentConfigArgs;
import com.pulumi.gcp.healthcare.inputs.FhirStoreConsentConfigConsentHeaderHandlingArgs;
import com.pulumi.gcp.healthcare.inputs.FhirStoreConsentConfigAccessDeterminationLogConfigArgs;
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 topic = new Topic("topic", TopicArgs.builder()
            .name("fhir-notifications")
            .build());

        var dataset = new Dataset("dataset", DatasetArgs.builder()
            .name("example-dataset")
            .location("us-central1")
            .build());

        var default_ = new FhirStore("default", FhirStoreArgs.builder()
            .name("example-fhir-store")
            .dataset(dataset.id())
            .version("R4")
            .complexDataTypeReferenceParsing("DISABLED")
            .enableUpdateCreate(false)
            .disableReferentialIntegrity(false)
            .disableResourceVersioning(false)
            .enableHistoryImport(false)
            .defaultSearchHandlingStrict(false)
            .notificationConfigs(FhirStoreNotificationConfigArgs.builder()
                .pubsubTopic(topic.id())
                .build())
            .labels(Map.of("label1", "labelvalue1"))
            .consentConfig(FhirStoreConsentConfigArgs.builder()
                .version("V1")
                .accessEnforced(true)
                .consentHeaderHandling(FhirStoreConsentConfigConsentHeaderHandlingArgs.builder()
                    .profile("REQUIRED_ON_READ")
                    .build())
                .accessDeterminationLogConfig(FhirStoreConsentConfigAccessDeterminationLogConfigArgs.builder()
                    .logLevel("VERBOSE")
                    .build())
                .build())
            .build());

    }
}
resources:
  default:
    type: gcp:healthcare:FhirStore
    properties:
      name: example-fhir-store
      dataset: ${dataset.id}
      version: R4
      complexDataTypeReferenceParsing: DISABLED
      enableUpdateCreate: false
      disableReferentialIntegrity: false
      disableResourceVersioning: false
      enableHistoryImport: false
      defaultSearchHandlingStrict: false
      notificationConfigs:
        - pubsubTopic: ${topic.id}
      labels:
        label1: labelvalue1
      consentConfig:
        version: V1
        accessEnforced: true
        consentHeaderHandling:
          profile: REQUIRED_ON_READ
        accessDeterminationLogConfig:
          logLevel: VERBOSE
  topic:
    type: gcp:pubsub:Topic
    properties:
      name: fhir-notifications
  dataset:
    type: gcp:healthcare:Dataset
    properties:
      name: example-dataset
      location: us-central1

The consentConfig block enables consent enforcement. The accessEnforced property requires consent evaluation for all access requests. The consentHeaderHandling property controls how consent headers are processed; setting profile to REQUIRED_ON_READ means clients must provide consent information in read requests. The accessDeterminationLogConfig property logs consent decisions at the specified verbosity level. Consent enforcement is not available for DSTU2 (no Consent resources) or R5 FHIR versions.

Disable FHIR validation for flexible data ingestion

During data migration or when working with non-standard FHIR implementations, strict validation can block legitimate data.

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

const topic = new gcp.pubsub.Topic("topic", {name: "fhir-notifications"});
const dataset = new gcp.healthcare.Dataset("dataset", {
    name: "example-dataset",
    location: "us-central1",
});
const _default = new gcp.healthcare.FhirStore("default", {
    name: "example-fhir-store",
    dataset: dataset.id,
    version: "R4",
    complexDataTypeReferenceParsing: "DISABLED",
    enableUpdateCreate: false,
    disableReferentialIntegrity: false,
    disableResourceVersioning: false,
    enableHistoryImport: false,
    defaultSearchHandlingStrict: false,
    notificationConfigs: [{
        pubsubTopic: topic.id,
    }],
    labels: {
        label1: "labelvalue1",
    },
    validationConfig: {
        disableProfileValidation: true,
        enabledImplementationGuides: [],
        disableRequiredFieldValidation: true,
        disableReferenceTypeValidation: true,
        disableFhirpathValidation: true,
    },
});
import pulumi
import pulumi_gcp as gcp

topic = gcp.pubsub.Topic("topic", name="fhir-notifications")
dataset = gcp.healthcare.Dataset("dataset",
    name="example-dataset",
    location="us-central1")
default = gcp.healthcare.FhirStore("default",
    name="example-fhir-store",
    dataset=dataset.id,
    version="R4",
    complex_data_type_reference_parsing="DISABLED",
    enable_update_create=False,
    disable_referential_integrity=False,
    disable_resource_versioning=False,
    enable_history_import=False,
    default_search_handling_strict=False,
    notification_configs=[{
        "pubsub_topic": topic.id,
    }],
    labels={
        "label1": "labelvalue1",
    },
    validation_config={
        "disable_profile_validation": True,
        "enabled_implementation_guides": [],
        "disable_required_field_validation": True,
        "disable_reference_type_validation": True,
        "disable_fhirpath_validation": True,
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/healthcare"
	"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 {
		topic, err := pubsub.NewTopic(ctx, "topic", &pubsub.TopicArgs{
			Name: pulumi.String("fhir-notifications"),
		})
		if err != nil {
			return err
		}
		dataset, err := healthcare.NewDataset(ctx, "dataset", &healthcare.DatasetArgs{
			Name:     pulumi.String("example-dataset"),
			Location: pulumi.String("us-central1"),
		})
		if err != nil {
			return err
		}
		_, err = healthcare.NewFhirStore(ctx, "default", &healthcare.FhirStoreArgs{
			Name:                            pulumi.String("example-fhir-store"),
			Dataset:                         dataset.ID(),
			Version:                         pulumi.String("R4"),
			ComplexDataTypeReferenceParsing: pulumi.String("DISABLED"),
			EnableUpdateCreate:              pulumi.Bool(false),
			DisableReferentialIntegrity:     pulumi.Bool(false),
			DisableResourceVersioning:       pulumi.Bool(false),
			EnableHistoryImport:             pulumi.Bool(false),
			DefaultSearchHandlingStrict:     pulumi.Bool(false),
			NotificationConfigs: healthcare.FhirStoreNotificationConfigArray{
				&healthcare.FhirStoreNotificationConfigArgs{
					PubsubTopic: topic.ID(),
				},
			},
			Labels: pulumi.StringMap{
				"label1": pulumi.String("labelvalue1"),
			},
			ValidationConfig: &healthcare.FhirStoreValidationConfigArgs{
				DisableProfileValidation:       pulumi.Bool(true),
				EnabledImplementationGuides:    pulumi.StringArray{},
				DisableRequiredFieldValidation: pulumi.Bool(true),
				DisableReferenceTypeValidation: pulumi.Bool(true),
				DisableFhirpathValidation:      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 topic = new Gcp.PubSub.Topic("topic", new()
    {
        Name = "fhir-notifications",
    });

    var dataset = new Gcp.Healthcare.Dataset("dataset", new()
    {
        Name = "example-dataset",
        Location = "us-central1",
    });

    var @default = new Gcp.Healthcare.FhirStore("default", new()
    {
        Name = "example-fhir-store",
        Dataset = dataset.Id,
        Version = "R4",
        ComplexDataTypeReferenceParsing = "DISABLED",
        EnableUpdateCreate = false,
        DisableReferentialIntegrity = false,
        DisableResourceVersioning = false,
        EnableHistoryImport = false,
        DefaultSearchHandlingStrict = false,
        NotificationConfigs = new[]
        {
            new Gcp.Healthcare.Inputs.FhirStoreNotificationConfigArgs
            {
                PubsubTopic = topic.Id,
            },
        },
        Labels = 
        {
            { "label1", "labelvalue1" },
        },
        ValidationConfig = new Gcp.Healthcare.Inputs.FhirStoreValidationConfigArgs
        {
            DisableProfileValidation = true,
            EnabledImplementationGuides = new() { },
            DisableRequiredFieldValidation = true,
            DisableReferenceTypeValidation = true,
            DisableFhirpathValidation = true,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.pubsub.Topic;
import com.pulumi.gcp.pubsub.TopicArgs;
import com.pulumi.gcp.healthcare.Dataset;
import com.pulumi.gcp.healthcare.DatasetArgs;
import com.pulumi.gcp.healthcare.FhirStore;
import com.pulumi.gcp.healthcare.FhirStoreArgs;
import com.pulumi.gcp.healthcare.inputs.FhirStoreNotificationConfigArgs;
import com.pulumi.gcp.healthcare.inputs.FhirStoreValidationConfigArgs;
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 topic = new Topic("topic", TopicArgs.builder()
            .name("fhir-notifications")
            .build());

        var dataset = new Dataset("dataset", DatasetArgs.builder()
            .name("example-dataset")
            .location("us-central1")
            .build());

        var default_ = new FhirStore("default", FhirStoreArgs.builder()
            .name("example-fhir-store")
            .dataset(dataset.id())
            .version("R4")
            .complexDataTypeReferenceParsing("DISABLED")
            .enableUpdateCreate(false)
            .disableReferentialIntegrity(false)
            .disableResourceVersioning(false)
            .enableHistoryImport(false)
            .defaultSearchHandlingStrict(false)
            .notificationConfigs(FhirStoreNotificationConfigArgs.builder()
                .pubsubTopic(topic.id())
                .build())
            .labels(Map.of("label1", "labelvalue1"))
            .validationConfig(FhirStoreValidationConfigArgs.builder()
                .disableProfileValidation(true)
                .enabledImplementationGuides()
                .disableRequiredFieldValidation(true)
                .disableReferenceTypeValidation(true)
                .disableFhirpathValidation(true)
                .build())
            .build());

    }
}
resources:
  default:
    type: gcp:healthcare:FhirStore
    properties:
      name: example-fhir-store
      dataset: ${dataset.id}
      version: R4
      complexDataTypeReferenceParsing: DISABLED
      enableUpdateCreate: false
      disableReferentialIntegrity: false
      disableResourceVersioning: false
      enableHistoryImport: false
      defaultSearchHandlingStrict: false
      notificationConfigs:
        - pubsubTopic: ${topic.id}
      labels:
        label1: labelvalue1
      validationConfig:
        disableProfileValidation: true
        enabledImplementationGuides: []
        disableRequiredFieldValidation: true
        disableReferenceTypeValidation: true
        disableFhirpathValidation: true
  topic:
    type: gcp:pubsub:Topic
    properties:
      name: fhir-notifications
  dataset:
    type: gcp:healthcare:Dataset
    properties:
      name: example-dataset
      location: us-central1

The validationConfig block controls FHIR validation rules. Setting disableProfileValidation, disableRequiredFieldValidation, disableReferenceTypeValidation, and disableFhirpathValidation to true relaxes validation, allowing ingestion of resources that don’t strictly conform to FHIR profiles. This is useful for migration scenarios where source data may not meet all FHIR requirements.

Beyond these examples

These snippets focus on specific FHIR store features: FHIR version selection and integrity controls, BigQuery streaming and Pub/Sub notifications, and consent enforcement and validation configuration. They’re intentionally minimal rather than full healthcare data platforms.

The examples reference pre-existing infrastructure such as Healthcare datasets, Pub/Sub topics for notifications, and BigQuery datasets for streaming exports. They focus on configuring the FHIR store rather than provisioning the surrounding infrastructure.

To keep things focused, common FHIR store patterns are omitted, including:

  • History import and modification controls (enableHistoryImport, enableHistoryModifications)
  • Search behavior configuration (defaultSearchHandlingStrict)
  • Client-specified resource IDs (enableUpdateCreate)
  • Labels and metadata organization

These omissions are intentional: the goal is to illustrate how each FHIR store feature is wired, not provide drop-in healthcare data modules. See the FHIR Store resource reference for all available configuration options.

Let's create GCP Healthcare FHIR Stores

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Data Loss & Immutability
What properties can't I change without losing data?
Changing name, disableReferentialIntegrity, disableResourceVersioning, or enableHistoryImport recreates the FHIR store and deletes all data. The dataset and version properties are also immutable. Plan these settings carefully during initial creation.
Can I enable history import without recreating the store?
Yes, enableHistoryImport can be changed manually in the Google Cloud Healthcare admin console without triggering store recreation, even though it’s marked as immutable in Pulumi.
What happens if I enable complex data type reference parsing?
Setting complexDataTypeReferenceParsing to ENABLED causes processing to fail if existing resources contain references to non-existent resources. Ensure all references are valid before enabling this feature.
Configuration & Setup
What IAM role do I need for streaming to BigQuery?
Before adding streamConfigs, grant the bigquery.dataEditor role to your project’s Cloud Healthcare Service Agent service account. Streaming results typically appear after a lag of dozens of seconds.
How many streaming configs can I have?
Each FHIR store supports up to 10 streaming configs. When you add a new config, subsequent resource mutations stream to the new location in addition to existing ones.
How do labels work in FHIR stores?
You can add up to 64 labels. The labels field is non-authoritative and only manages labels in your configuration. Use effectiveLabels to see all labels present on the resource, including those set by other clients.
FHIR Version Compatibility
What FHIR versions are supported and what are their limitations?
Supported versions are DSTU2, STU3 (default), and R4. Consent enforcement (consentConfig) isn’t available for DSTU2 due to absence of Consent resources, and isn’t supported for R5.
Notifications & Streaming
Should I use notificationConfig or notificationConfigs?
Use notificationConfigs (plural). The singular notificationConfig is deprecated and will be removed in a future major release.
Security & Search Behavior
What's the difference between strict and lenient search handling?
Setting defaultSearchHandlingStrict to true returns errors for unrecognized search parameters (strict mode). Setting it to false ignores unrecognized parameters (lenient mode, FHIR specification default). You can override this per-request using the HTTP header Prefer: handling=strict or Prefer: handling=lenient.
What security considerations exist for client-specified resource IDs?
When enableUpdateCreate is true, client-specified IDs may contain sensitive data like patient identifiers. These IDs appear in FHIR resource paths recorded in Cloud audit logs and Pub/Sub notifications, so treat audit logs with appropriate care.

Using a different cloud?

Explore database guides for other cloud providers: