Skip to content

HealthLake

Note: This documentation is also available in a rendered format here.

Deploys one or more Amazon HealthLake FHIR R4 datastores with customer-managed KMS encryption, per-datastore least-privilege IAM data-access roles for import/export operations, and automatic Glue database metadata resolution. Datastores are configured via a named datastores map — each entry becomes a distinct datastore, all sharing a single KMS key. Use this module when you need one or more compliant, production-ready HealthLake datastores for healthcare data interoperability.


Deployed Resources

Per entry in the datastores map:

HealthLake FHIR R4 Datastore - A fully managed FHIR R4-compliant datastore encrypted with the (shared) customer-managed KMS key for storing and querying healthcare data.

Datastore Replacement Guard - A Custom Resource (Lambda-backed) that blocks CloudFormation updates which would replace (delete and recreate) the datastore — see Datastore Replacement Protection.

IAM Data-Access Role - A least-privilege IAM role assumed by the HealthLake service for S3 read/write and KMS encrypt/decrypt operations during import and export jobs.

Glue Database Resolver - A construct that derives the auto-created Glue database metadata (name and catalog ID) after datastore creation.

SSM Parameters - Nine SSM parameters per datastore: datastore ID, ARN, and endpoint; the data-access role's ARN, ID, and name (plus a healthlake-namespaced role-ARN alias); and the Glue database name and catalog ID — all for cross-module consumption.

Shared across all datastores in the stack:

KMS Key - A single customer-managed KMS key is used to encrypt every datastore. Provide kmsKeyArn at the module root to reuse an existing key, or omit it to have the module create one.


Security/Compliance Details

This module enforces compliance by default with no opt-out for critical security controls:

  • Encryption at Rest - Customer-managed KMS key (CMK) encryption is mandatory; no unencrypted datastores can be deployed
  • Least Privilege - IAM policies are scoped to specific ARNs only; no wildcard resources (*) are used anywhere
  • KMS Grant Control - kms:CreateGrant is restricted with the GrantIsForAWSResource condition
  • Service Trust - Only healthlake.amazonaws.com is permitted in the role trust policy
  • CDK Nag Validation - All resources pass AwsSolutions, NIST 800-53, HIPAA, and PCI DSS rulesets

Datastore Replacement Protection

AWS::HealthLake::FHIRDatastore replaces (deletes and recreates) the underlying datastore whenever DatastoreName, DatastoreTypeVersion, IdentityProviderConfiguration, PreloadDataConfig, or SseConfiguration changes — this is documented AWS CloudFormation behavior, not an MDAA limitation. In practice this means renaming a datastore entry (its datastores map key), changing kmsKeyArn, toggling identityProviderConfiguration, or toggling preloadSynthea on an existing datastore would trigger a replacement.

This module blocks that from happening by default. Each datastore is guarded by a Custom Resource that compares the previous and current values of datastoreName, kmsKeyArn, identityProviderConfiguration, and preloadSynthea on every deploy. If any of these changed, the deployment fails before the datastore is touched — no replacement occurs, no data is at risk. RemovalPolicy.RETAIN is also applied to every FHIRDatastore as defense-in-depth: if a replacement is explicitly acknowledged (see below), the old datastore is detached rather than deleted, surviving as an orphaned AWS resource whose data you must migrate manually.

If you intend to make one of these changes on purpose (e.g. provisioning a genuinely new datastore under an existing name), set acknowledgeReplacement: true on that datastore's configuration for the deploy that makes the change:

datastores:
  primary:
    rawBucketArn: arn:{{partition}}:s3:::example-raw-bucket
    preloadSynthea: true
    acknowledgeReplacement: true  # required to allow this change through

Remove acknowledgeReplacement (or set it back to false) after the acknowledged deploy completes, so the guard resumes blocking unintentional changes.


Configuration

MDAA Config

# mdaa.yaml
healthlake:
  module: "@aws-mdaa/healthlake"
  config: healthlake-config.yaml

Module Config Samples and Variants

Minimal Configuration

Deploys a HealthLake FHIR R4 datastore with only the required S3 bucket ARN. A customer-managed KMS key is created automatically when not explicitly provided. Use this when you need a basic datastore without sample data or SMART on FHIR authorization.

sample-config-minimal.yaml

# HealthLake FHIR R4 Datastore - Minimal Configuration
# Deploys a single HealthLake FHIR R4 datastore with auto-created customer-managed KMS encryption.
# A KMS key is created automatically when kmsKeyArn is not specified.

# Roles granted administer/use on the auto-created KMS key. Required when the key is
# auto-created (kmsKeyArn omitted): the key is retained on stack teardown, so at least
# one scoped admin role must be provided to keep it manageable.
dataAdminRoles:
  - arn: arn:{{partition}}:iam::{{account}}:role/example-data-admin

# Named map of HealthLake datastores. Each map key is the datastore name suffix.
datastores:
  primary:
    # S3 bucket ARN for HealthLake data access (required)
    rawBucketArn: arn:{{partition}}:s3:::example-raw-bucket

Comprehensive Configuration

Covers all available configuration options including explicit KMS key, Synthea sample data preloading, and SMART on FHIR identity provider configuration. Use this as a reference for the full set of configurable properties.

sample-config-comprehensive.yaml

# HealthLake FHIR R4 Datastore - Comprehensive Configuration
# Demonstrates multiple named datastores in a single stack, with all available options
# exercised on the primary datastore. A single shared KMS key is used across datastores.

# (Optional) KMS key ARN shared across all datastores.
# If not provided, a customer-managed KMS key is created automatically.
kmsKeyArn: arn:{{partition}}:kms:{{region}}:{{account}}:key/example-key-id

# Named map of HealthLake datastores. Each map key is the datastore name suffix.
datastores:
  primary:
    # S3 bucket ARN for HealthLake data access (required)
    rawBucketArn: arn:{{partition}}:s3:::example-raw-bucket-primary
    # (Optional) Whether to preload Synthea sample FHIR data (default: false)
    preloadSynthea: true
    # (Optional) Identity provider configuration for SMART on FHIR authorization.
    # When omitted, defaults to AWS_AUTH (IAM Signature v4).
    identityProviderConfiguration:
      # Authorization strategy (enum: AWS_AUTH, SMART_ON_FHIR_V1, SMART_ON_FHIR)
      authorizationStrategy: SMART_ON_FHIR
      # (Optional) Enable fine-grained authorization (default: false)
      fineGrainedAuthorizationEnabled: true
      # (Optional) ARN of Lambda to decode OAuth2 access tokens
      idpLambdaArn: arn:{{partition}}:lambda:{{region}}:{{account}}:function:smart-token-decoder
      # (Optional) JSON metadata for SMART App Launch specification
      metadata: '{"authorization_endpoint":"https://auth.example.com/authorize","token_endpoint":"https://auth.example.com/token","grant_types_supported":["authorization_code","client_credentials"],"capabilities":["launch-standalone","client-public","client-confidential-symmetric","permission-v2","context-standalone-patient"],"code_challenge_methods_supported":["S256"]}'
    # (Optional) Set to true only on the deploy that intentionally changes datastoreName,
    # kmsKeyArn, identityProviderConfiguration, or preloadSynthea on an existing datastore.
    # These changes cause AWS::HealthLake::FHIRDatastore to be replaced (deleted and
    # recreated); this module blocks such changes by default (default: false).
    acknowledgeReplacement: false
  secondary:
    rawBucketArn: arn:{{partition}}:s3:::example-raw-bucket-secondary

SMART on FHIR Configuration

Deploys a HealthLake FHIR R4 datastore with SMART on FHIR authorization enabled, requiring an OAuth2 token-decoding Lambda ARN. Use this when integrating with a SMART App Launch identity provider for clinical applications or third-party EHR clients.

sample-config-smart.yaml

# HealthLake FHIR R4 Datastore - SMART on FHIR Configuration
# Deploys a HealthLake FHIR R4 datastore with SMART on FHIR authorization for
# clinical application access (patient portals, EHR integrations, third-party SMART apps).

# Roles granted administer/use on the auto-created KMS key. Required when the key is
# auto-created (kmsKeyArn omitted): the key is retained on stack teardown, so at least
# one scoped admin role must be provided to keep it manageable.
dataAdminRoles:
  - arn: arn:{{partition}}:iam::{{account}}:role/example-data-admin

# Named map of HealthLake datastores. Each map key is the datastore name suffix.
datastores:
  primary:
    # S3 bucket ARN for HealthLake data access (required)
    rawBucketArn: arn:{{partition}}:s3:::example-raw-bucket

    # Identity provider configuration for SMART on FHIR authorization
    identityProviderConfiguration:
      # Authorization strategy (enum: AWS_AUTH, SMART_ON_FHIR_V1, SMART_ON_FHIR)
      # SMART_ON_FHIR supports both V1 and V2 with full CRUD+search permissions
      authorizationStrategy: SMART_ON_FHIR
      # (Optional) Enable fine-grained authorization for scoped access per patient/resource
      fineGrainedAuthorizationEnabled: true
      # ARN of the Lambda function that decodes OAuth2 access tokens from your authorization server
      idpLambdaArn: arn:{{partition}}:lambda:{{region}}:{{account}}:function:smart-token-decoder
      # JSON metadata for SMART App Launch specification
      # Must include: authorization_endpoint, grant_types_supported, token_endpoint,
      # capabilities, code_challenge_methods_supported (must include S256)
      metadata: '{"authorization_endpoint":"https://auth.example.com/authorize","token_endpoint":"https://auth.example.com/token","grant_types_supported":["authorization_code","client_credentials"],"capabilities":["launch-standalone","client-public","client-confidential-symmetric","permission-v2","context-standalone-patient"],"code_challenge_methods_supported":["S256"]}'