# infra/azure — the Entra half of the SharePoint source

`sharepoint-entra.tf.sample` is the app registration, consent, certificate and AWS handoff that the
SharePoint data source in `../knowledgebase.yml` depends on. It is the **verified shape** — the
configuration behind a knowledge base that was observed ingesting and answering in this account —
and it is still a `.tf.sample`, deliberately.

The operational walkthrough is `../../docs/sharepoint-source.md`. This file is only about why this
directory is shaped the way it is.

## Why it isn't a `.tf`

**Nothing would run it.** `pipeline/validate_stacks.py` cross-checks every
`blueprints/*/infra/azure/` directory holding `.tf` files against the `TF_WORKING_DIR` values in
`pipeline/pipeline.yml`, in both directions. A module with no action fails PR checks — which is the
protection working, not an obstacle: a `.tf` here with no action would imply a deploy path that
does not exist. `blueprints/entra-probe/infra/azure/` is the worked example of a module that has
one.

**And it reads local files.** The certificate is generated by hand — CloudFormation cannot produce
a `.p12`, and a certificate password in a template is a secret in a public repo with no secret
scanning. So the module reads `.certs/` off disk, which an unattended CodeBuild run does not have.
Automating that means staging the certificate somewhere the build can fetch it, i.e. the same
problem one layer down.

Wiring it would therefore be: add the `Terraform` stage action, rename the file, and solve
certificate delivery. The first two are mechanical. The third is the actual decision.

## What it does, and what stays manual

| | Where |
|---|---|
| App registration, service principal, `Sites.Selected` on both resource apps, admin consent | This module |
| Certificate registered with Entra, `.p12` in S3, secret with `clientId` + `certificatePassword` | This module |
| Throwaway `Sites.FullControl.All` granter, gated by `create_granter` | This module |
| Generating the certificate | By hand — `openssl`, see `../../docs/sharepoint-source.md` |
| The per-site Graph grant | By hand — a `POST` this module cannot express |
| Proving it works before Bedrock sees it | By hand — the standalone token/roles/drive check |

Three of those six being manual is the honest state of it, and the last one is the one that
matters: Bedrock's errors do not tell you which half is wrong.

## Two things that are easy to get wrong here

**Both resource applications need a grant.** Microsoft Graph
(`00000003-0000-0000-c000-000000000000`) *and* the SharePoint REST API
(`00000003-0000-0ff1-ce00-000000000000`). Graph alone produces a token that reads site metadata and
fails on content.

**Consent is `azuread_app_role_assignment`.** Not
`azuread_service_principal_delegated_permission_grant`, which is delegated-only and silently fails
to consent a `type = "Role"` permission. And the module resolves app role ids from
`data.azuread_service_principal.*.app_role_ids` rather than hardcoding GUIDs, because several are
near-identical by eye — copying one from notes is exactly how a `Sites.Read.All` + `User.Read.All`
over-grant happens when `Files.Read.All` was meant.

## Credentials

Nothing in this directory contains one, and nothing should. The `.p12`, its password and the
private key live in a gitignored `.certs/` and in AWS; the module references them by path and hands
AWS an ARN and an S3 key. The p12 password also lands in **Terraform state**, so `*.tfstate*` stays
gitignored for the same reason.

`dev/workshop/entra/sharepoint` (keys `entraAppID`, `entraAppDirectoryID`, `entraAppSecretID`,
`entraAppSecretValue`) is the workshop's original **client-secret** credential. `ENTRA_ID_APP_ONLY`
cannot use a client secret, so nothing here reads it. The connector's secret is
`bedrock/sharepoint-cert-connector`.

This repo is public and **has no secret scanning** — an enforced org security configuration
disables it. Nothing mechanical stops a committed credential.

## The much smaller win, still

The web crawler needs none of this: no Entra, no certificate, no Terraform. Another
`AWS::Bedrock::DataSource` with `type: WEB` in `ConnectorParameters`, plus a verifier instance
copied from `SharePointIngestionVerifier`. If the goal is another source rather than *SharePoint*,
that is the cheap version.

## The generalized walkthrough

`../../docs/sharepoint-runbook.md` is this path validated end to end on a **separate tenant and AWS
account** — certificate generation, the two resource applications, the per-site grant, the Secrets
Manager and IAM shapes, and a pre-flight script that separates an Entra fault from a Bedrock one
before any Bedrock resource exists.

Read it as the generalized reference and this directory as the Cornell-specific instance. It also
settles the consent question these files used to leave open: `Sites.Selected` on two resource
applications plus one per-site grant is sufficient, as long as `aclEnabled` is `false`.
