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 that stores and manages healthcare data conforming to FHIR standards (DSTU2, STU3, or R4). This guide focuses on three capabilities: Pub/Sub notifications for resource mutations, BigQuery streaming for analytics, and consent enforcement and validation controls.

FHIR stores belong to Healthcare datasets and may 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 FHIR deployments start with a basic store that tracks resource mutations through Pub/Sub notifications, enabling downstream systems to react to patient data 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 notificationConfigs array sends change events to Pub/Sub topics whenever resources are created, updated, or deleted. The complexDataTypeReferenceParsing property controls whether references within complex data types (like Extensions) are parsed for referential integrity checks.

Stream FHIR resources to BigQuery for analytics

Healthcare analytics pipelines often stream FHIR resources into BigQuery as they’re created or modified, enabling SQL-based analysis of patient data.

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 streaming 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, including recursiveStructureDepth for nested fields and lastUpdatedPartitionConfig for time-based partitioning. Before adding streaming configs, you must grant the bigquery.dataEditor role to your project’s Cloud Healthcare Service Agent.

Configure detailed notification payloads

Some workflows need the full resource content in notifications rather than just change events, or need to track what was deleted.

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 complete FHIR resource in each notification message, not just metadata about the change. The sendPreviousResourceOnDelete property sends the deleted resource’s content when resources are removed, enabling audit trails and recovery workflows.

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

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 verification for all access requests. The consentHeaderHandling property controls how consent headers are processed (REQUIRED_ON_READ means clients must provide consent information). The accessDeterminationLogConfig property logs consent decisions at VERBOSE level for audit purposes. Consent enforcement is not available for DSTU2 FHIR version due to absence of Consent resources.

Disable FHIR validation for flexible ingestion

During migrations or when ingesting data from legacy systems, strict FHIR validation can block otherwise usable resources.

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 behavior. Setting disableProfileValidation, disableRequiredFieldValidation, disableReferenceTypeValidation, and disableFhirpathValidation to true allows non-conformant resources to be stored. This is useful during data migrations when source systems produce FHIR resources that don’t fully conform to specification requirements.

Beyond these examples

These snippets focus on specific FHIR store features: Pub/Sub notifications and BigQuery streaming, consent enforcement and validation controls, and FHIR version selection and reference parsing. 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 destinations. They focus on configuring the FHIR store rather than provisioning the surrounding infrastructure.

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

  • Referential integrity controls (disableReferentialIntegrity)
  • Resource versioning behavior (disableResourceVersioning)
  • History import capabilities (enableHistoryImport)
  • Client-specified IDs (enableUpdateCreate)
  • Search parameter handling (defaultSearchHandlingStrict)

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
Which properties will recreate my FHIR store and delete all data?
Changing name, disableReferentialIntegrity, disableResourceVersioning, or enableHistoryImport recreates the store and removes all data. Plan these settings carefully during initial creation.
Can I change enableHistoryImport without losing data?
Yes, enableHistoryImport can be changed manually in the Google Cloud Healthcare admin console without recreating the FHIR store, even though Pulumi treats it as immutable.
FHIR Versioning & Compatibility
What FHIR versions are supported?
DSTU2, STU3 (default), and R4. The version property is immutable after creation.
Can I use consent enforcement with all FHIR versions?
No, consentConfig is not available for DSTU2 (due to absence of Consent resources) and not supported for R5.
What happens if I enable complexDataTypeReferenceParsing on an existing store?
Setting it to ENABLED causes processing existing resources to fail if they contain references to non-existent resources. Ensure all references are valid before enabling.
Streaming & BigQuery Integration
What IAM role do I need for streaming FHIR data to BigQuery?
Add the bigquery.dataEditor role to your project’s Cloud Healthcare Service Agent service account before configuring streamConfigs.
How long does it take for streamed data to appear in BigQuery?
Expect lag typically on the order of dozens of seconds before results show up in the streaming destination.
How many streaming configurations can I have?
Each FHIR store is allowed up to 10 streaming configs in streamConfigs.
Notifications & Pub/Sub
Should I use notificationConfig or notificationConfigs?
Use notificationConfigs. The notificationConfig property is deprecated and will be removed in a future major release.
How do I send full FHIR resources in Pub/Sub notifications?
Set sendFullResource to true in your notificationConfigs. You can also enable sendPreviousResourceOnDelete to include the resource state before deletion.
Security & Compliance
What's the security risk with enableUpdateCreate?
Client-specified resource IDs (which may contain patient identifiers) will be part of the FHIR resource path recorded in Cloud audit logs and Cloud Pub/Sub notifications. Treat audit logs with appropriate care.
What happens when I disable referential integrity?
When disableReferentialIntegrity is true, the API skips referential integrity checks. Operations like Patient.get$everything may not return all results if broken references exist.
Labels & Metadata
What are the requirements for labels?
Label keys must be 1-63 characters matching regex [\p{Ll}\p{Lo}][\p{Ll}\p{Lo}\p{N}_-]{0,62}. Values must be 1-63 characters matching [\p{Ll}\p{Lo}\p{N}_-]{0,63}. Maximum 64 labels per store. The labels field is non-authoritative; use effectiveLabels to see all labels.

Using a different cloud?

Explore database guides for other cloud providers: