# Manage the Reader Role Trust Policy

The table reader IAM role has a trust policy that controls which external services may assume it — Databricks, Snowflake, or BigQuery. This page shows you how to read the current policy and add a new integration without removing the ones already configured.

The update endpoint replaces the **entire** policy rather than merging into it. If you post only the entry you are adding, every other integration is dropped and stops working. Whenever an account already has an integration, add a new one with a read-modify-write flow: get the current policy, add your entry, then post the combined list.

## Prerequisites

- Iceberg catalog resources provisioned and `active` for your account (see [Provision Resources](/products/customer-data-platform/composable-publish/resource-provisioning))
- The trust policy details for the integration you are adding — collected during that integration's setup ([Databricks](/products/customer-data-platform/composable-publish/databricks-integration), [Snowflake](/products/customer-data-platform/composable-publish/snowflake-integration), or [BigQuery](/products/customer-data-platform/composable-publish/bigquery-integration))


## Authentication

For authentication details and API endpoints by site, see [Use Iceberg Catalog Management API](/products/customer-data-platform/composable-publish/use-iceberg-catalog-api). All trust policy operations are admin-only, and write-only API keys are rejected. Examples use the US endpoint — replace it with your site's endpoint.

## Trust Policy Entries

A trust policy is a list of entries, one per service. Each entry has the same shape across both the get and update endpoints:

| Field | Description |
|  --- | --- |
| `service` | The service allowed to assume the role: `databricks`, `snowflake`, or `bigquery` |
| `iam_principal_arns` | IAM principal ARNs allowed to assume the role. Required for `databricks` and `snowflake`; omit for `bigquery`. Up to 5. |
| `external_id` | External ID for the assume-role condition. |


A policy holds **at most one entry per service**. Posting two entries for the same service is rejected.

## Get the Current Trust Policy

Retrieve the entries currently configured for the reader role:

```sh
curl "https://api-iceberg-mng.us01.treasuredata.com/v1/iceberg/catalog/resources/reader_role/trust_policy" \
  -H "Authorization: TD1 <admin_api_key>" \
  -H "Accept: application/json"
```

The response returns `200` with the configured entries. For example, an account that already trusts Databricks returns:

```json
{
  "entries": [
    {
      "service": "databricks",
      "iam_principal_arns": [
        "arn:aws:iam::414351767826:role/unity-catalog-prod-UCMasterRole-14S5ZJVKOTYTL",
        "<iam_role_arn>"
      ],
      "external_id": "<databricks_external_id>"
    }
  ]
}
```

An account with no entries configured yet returns `200` with an empty list (`{ "entries": [] }`). A `404` means resources are not provisioned for the account — provision them first.

## Append a New Integration

1. Get the current policy with the request above and note every existing entry.
2. Post the combined list: every existing entry, plus the entry for the new integration. This example adds Snowflake to an account that already trusts Databricks:


```sh
curl -X POST "https://api-iceberg-mng.us01.treasuredata.com/v1/iceberg/catalog/resources/reader_role/trust_policy" \
  -H "Authorization: TD1 <admin_api_key>" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "entries": [
      {
        "service": "databricks",
        "iam_principal_arns": [
          "arn:aws:iam::414351767826:role/unity-catalog-prod-UCMasterRole-14S5ZJVKOTYTL",
          "<iam_role_arn>"
        ],
        "external_id": "<databricks_external_id>"
      },
      {
        "service": "snowflake",
        "iam_principal_arns": ["<snowflake_iam_user_arn>"],
        "external_id": "<snowflake_external_id>"
      }
    ]
  }'
```

The endpoint returns `202 Accepted` and applies the policy asynchronously by updating the underlying stack.

Important
Omitting an existing entry removes that integration's access. Always start from the output of the get endpoint so no entry is lost.

## Remove an Integration

To revoke a service's access, post the policy without its entry. To remove all access, post an empty list:

```sh
curl -X POST "https://api-iceberg-mng.us01.treasuredata.com/v1/iceberg/catalog/resources/reader_role/trust_policy" \
  -H "Authorization: TD1 <admin_api_key>" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{ "entries": [] }'
```

## Verify the Result

Call the get endpoint again to confirm the policy contains exactly the entries you intend.

The get endpoint returns the **intended** policy — the value the service persisted — which during an in-flight update may not yet match the live IAM state. To confirm the update has fully applied, poll the [Get Resource Status](/products/customer-data-platform/composable-publish/resource-provisioning#get-resource-status) endpoint and wait for `status` to return to `active`.

## Troubleshooting

| Symptom | Possible Cause |
|  --- | --- |
| An existing integration stops working after an update | Its entry was omitted from the posted list — re-post with all entries included |
| `400 Invalid trust policy` | A duplicate `service` entry, an unsupported field for the service, or a malformed ARN or external ID |
| `404` | Resources are not provisioned for the account |
| `409` | Resources are not in an updatable state — wait for `status` to return to `active` |


## Next Steps

- [Integrate with Databricks](/products/customer-data-platform/composable-publish/databricks-integration)
- [Integrate with Snowflake](/products/customer-data-platform/composable-publish/snowflake-integration)
- [Integrate with BigQuery](/products/customer-data-platform/composable-publish/bigquery-integration)