> The complete documentation index is at [llms.txt](/llms.txt).

Connect BigQuery to Bauplan by creating external Iceberg tables in BigQuery that point to your Bauplan tables in Google Cloud Storage (GCS). This lets you query Bauplan-managed data from BigQuery without copying it.

BigQuery reads the objects in your GCS bucket through a Cloud resource connection, and uses the Iceberg metadata file of each table to discover its schema and data files.

> BigQuery cannot attach to an external Iceberg REST catalog such as Bauplan's,
>   so tables are registered one by one. This pattern relies on a BigQuery [Cloud
>   resource
>   connection](https://docs.cloud.google.com/bigquery/docs/create-cloud-resource-connection),
>   whose service account is granted read access to the bucket, and a `CREATE
>   EXTERNAL TABLE` statement that points to the current Iceberg metadata file of
>   the table. See the [BigQuery
>   documentation](https://docs.cloud.google.com/bigquery/docs/iceberg-external-tables)
>   for the details of Iceberg external tables.

## When to use this integration

- You want BigQuery users to run SQL over Bauplan tables that live in GCS.
- You prefer read access from BigQuery without moving data.
- You deploy and evolve tables in Bauplan, then surface them in BigQuery for analytics.

## Prerequisites

- A GCS bucket that stores your Bauplan table data and Iceberg metadata.
- A GCP account with permissions to grant IAM roles on that bucket.
- A BigQuery role with privileges to create connections and external tables.

## Step 1. Create the dataset and the connection

Create a BigQuery [dataset](https://docs.cloud.google.com/bigquery/docs/datasets) and a Cloud resource connection, both in the same location as your bucket. BigQuery creates a service account for the connection: grant it the Storage Object Viewer role (`roles/storage.objectViewer`) on the Bauplan bucket. BigQuery only reads from the bucket, so read access is enough. Follow the [BigQuery documentation](https://docs.cloud.google.com/bigquery/docs/create-cloud-resource-connection) for the exact steps.

## Step 2. Register the tables

Bauplan-managed tables can be registered "zero-copy" into BigQuery by issuing the following SQL command, where:

- `BQ_FULL_TABLE` is the fully qualified name (`project.dataset.table`) for the table you want to create.
- `BQ_CONNECTION` is the fully qualified name (`project.location.connection`) of the connection created above.
- `metadata_uri` is the full `gs://` path of the JSON metadata file of the Bauplan table you want to register.

```sql
CREATE OR REPLACE EXTERNAL TABLE `{BQ_FULL_TABLE}`
WITH CONNECTION `{BQ_CONNECTION}`
OPTIONS (
    format = 'ICEBERG',
    uris = ["{metadata_uri}"]
)
```

The registered table is pinned to the snapshot described by that metadata file. Bauplan (through its [Git-for-data](/concepts/git-for-data/data-branches) abstractions) can hold multiple versions of the same table in different branches, and a pipeline that runs every day naturally advances the snapshot of its final tables. In practice, this means you should run the registration command every time the underlying snapshot changes. In your outer loop, cron, or orchestrator, the workflow looks like this:

1. A Bauplan DAG is triggered on a job-related branch, replacing `my_table`.
2. On success, the branch is merged and you now have a new version of `my_table`.
3. The `metadata_location` of the latest version of `my_table` (just merged into `main`) is retrieved, and the registration command is issued to keep BigQuery in sync.

```python
from google.cloud import bigquery
import bauplan

BQ_FULL_TABLE = "project.dataset.my_table"
BQ_CONNECTION = "project.location.my_connection"
branch = "my_user.my_branch"

bpln_client = bauplan.Client()
bq_client = bigquery.Client()

# .... create the development branch etc. ....
run_state = bpln_client.run('my_project/', ref=branch)
# .... check if it is a successful run, merge the branch, etc. ....
metadata_location = bpln_client.get_table('my_table', ref='main').metadata_location
ddl = f"""
   CREATE OR REPLACE EXTERNAL TABLE `{BQ_FULL_TABLE}`
   WITH CONNECTION `{BQ_CONNECTION}`
   OPTIONS (
       format = 'ICEBERG',
       uris = ["{metadata_location}"]
   )
"""
bq_client.query(ddl).result()
print("BigQuery table registered.")
```

> BigQuery treats these tables as read-only: writes remain managed by Bauplan.
>   Retrieving `metadata_location` only needs read access, so the
>   registration step can run with the API key of a read-only Bauplan user.