> ## Documentation Index
> Fetch the complete documentation index at: https://getfloo.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Query a private BigQuery dataset

> Let a floo app query BigQuery behind your VPC Service Controls perimeter, using the app's own identity and no key.

Each environment of a floo app runs as its own Google service account. Grant that account access to your dataset and admit it through your perimeter, and the BigQuery client in your app authenticates on its own. There is no key to create, store, or rotate.

## 1. Read your app's identity

```bash theme={null}
floo apps show --app my-app --json | jq -r '.data.envs[] | "\(.name) \(.runtime_service_account)"'
```

```text theme={null}
dev floo-0123456789abcdef0123-np@<project>.iam.gserviceaccount.com
prod floo-0123456789abcdef0123@<project>.iam.gserviceaccount.com
```

Dev and previews share one identity; prod has its own. Grant both if dev should reach the data. `null` means that environment has no dedicated identity to grant. Deploy to that environment and read it again; if it is still `null`, email [team@getfloo.com](mailto:team@getfloo.com?subject=App%20runtime%20identity).

The identity belongs to the app. Deleting and recreating an app gives it a new identity, so read the value from `floo apps show` instead of copying it into other systems by hand.

## 2. Grant BigQuery roles in your project

Run these in your own project. Replace `YOUR_PROJECT`, `YOUR_DATASET`, and `APP_SA` with your values.

Read access to one dataset:

```sql theme={null}
GRANT `roles/bigquery.dataViewer`
ON SCHEMA `YOUR_PROJECT.YOUR_DATASET`
TO "serviceAccount:APP_SA";
```

Permission to run query jobs in your project:

```bash theme={null}
gcloud projects add-iam-policy-binding YOUR_PROJECT \
  --member="serviceAccount:APP_SA" \
  --role="roles/bigquery.jobUser"
```

If either grant fails with "One or more users named in the policy do not belong to a permitted customer", your organization enforces domain-restricted sharing, which blocks roles for identities outside your organization. Which constraint enforces it decides the fix:

* `iam.managed.allowedPolicyMembers`: add `serviceAccount:APP_SA` to its `allowedMemberSubjects` list.
* `iam.allowedPolicyMemberDomains`, the legacy constraint, on by default for organizations created since May 3, 2024: it accepts only whole organizations or Workspace customers, not single accounts. Migrate to `iam.managed.allowedPolicyMembers`, then add the account as above.

Do not allow floo's whole organization in either constraint: that admits every service account floo runs, not only yours. See [restricting identities by domain](https://docs.cloud.google.com/organization-policy/restrict-domains).

## 3. Admit the identity through your perimeter

Add an ingress rule for the app's identity. `accessLevel: "*"` allows it from any network, which is required because an app has no fixed source IP.

```yaml ingress.yaml theme={null}
- ingressFrom:
    identities:
    - serviceAccount:APP_SA
    sources:
    - accessLevel: "*"
  ingressTo:
    operations:
    - serviceName: bigquery.googleapis.com
      methodSelectors:
      - method: "*"
    resources:
    - projects/YOUR_PROJECT_NUMBER
```

`--set-ingress-policies` takes the perimeter's complete list of ingress rules. Put your existing rules in the same file, or they are removed.

```bash theme={null}
gcloud access-context-manager perimeters update YOUR_PERIMETER \
  --policy=YOUR_ACCESS_POLICY_ID \
  --set-ingress-policies=ingress.yaml
```

Perimeter changes take up to 30 minutes to apply. Details: [ingress and egress rules](https://docs.cloud.google.com/vpc-service-controls/docs/ingress-egress-rules).

## 4. Run query jobs in your project

Set the client's project to your project. A query job counts as inside the perimeter only when it runs in a project inside the perimeter. Without an explicit project, the client runs the job in the app identity's own project, outside your perimeter, and the ingress rule above does not admit it.

Set the project per environment, so dev can query a different project from prod:

```bash theme={null}
floo env set BIGQUERY_PROJECT=YOUR_PROJECT --app my-app --env prod
floo env set BIGQUERY_PROJECT=YOUR_DEV_PROJECT --app my-app --env dev
```

Then pass it to the client:

```python theme={null}
import os

from google.cloud import bigquery

client = bigquery.Client(project=os.environ["BIGQUERY_PROJECT"])
rows = client.query("SELECT COUNT(*) AS n FROM `YOUR_PROJECT.YOUR_DATASET.events`").result()
print(next(iter(rows)).n)
```

```javascript theme={null}
import { BigQuery } from "@google-cloud/bigquery";

const bigquery = new BigQuery({ projectId: process.env.BIGQUERY_PROJECT });
const [rows] = await bigquery.query(
  "SELECT COUNT(*) AS n FROM `YOUR_PROJECT.YOUR_DATASET.events`",
);
console.log(rows[0].n);
```

Deploy, then run the query from the deployed app. On your laptop the client uses your own credentials, not the app's identity.

## When a query is refused

| Error | Fix |
| - | - |
| `Request is prohibited by organization's policy` with a `vpcServiceControlsUniqueIdentifier` | The perimeter refused it. Check that the ingress rule names the right identity and project number, that the client sets your project, and that 30 minutes have passed. |
| `Access Denied: ... User does not have bigquery.jobs.create permission` | Grant `roles/bigquery.jobUser` on the project the client sets. |
| `Access Denied: Table ...` | Grant `roles/bigquery.dataViewer` on the dataset. |

## Related guides

* [Networking and egress](https://getfloo.com/docs/guides/networking.md)
* [Environment variables](https://getfloo.com/docs/guides/environment-variables.md)
* [Environments](https://getfloo.com/docs/guides/environments.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.