● Tenant-neutral template — make it yours before deploying

Manage Microsoft Purview
as version-controlled code.

A declarative, reviewable definition of an entire Microsoft Purview environment for a single tenant — both the Azure resource and the governance catalog — deployed automatically by GitHub Actions. Everything is a pull request.

What is this?

Infrastructure-as-Code for data governance

Microsoft Purview does not ship a single "Purview-as-Code" product. This repository fills that gap: it expresses your Purview configuration as code so every change is diff-able, reviewable in a pull request, and applied by an idempotent, auditable pipeline — never by hand-clicking in a portal.

📝

Declarative

Human-readable YAML and Bicep describe the desired state. The repo, not the portal, is the source of truth.

🔁

Idempotent

Every apply script reads current state, diffs it, then upserts. Re-running changes nothing that already matches.

🔐

Least privilege

GitHub Actions authenticates to Azure with short-lived OIDC tokens — no long-lived secrets stored anywhere.

🧭

Reviewable

A pull request is the change-review process. Control-plane and data-plane changes never mix in one PR.

🤖

Agent-assisted

A Squad of workspace-scoped Copilot agents intakes ideas, drafts issues, implements, and gates on owner approval.

📚

Learn-grounded

Every resource, cmdlet, and API call cites a current Microsoft Learn page — model recall alone is never enough.

The core idea

Two planes, kept deliberately separate

A complete Purview IaC repo must manage two independent planes, each with its own API surface and authentication model. A change almost always lives in exactly one of them.

Control plane

The Azure resource

The Microsoft.Purview/accounts resource, networking, managed identity, and SKU — defined as Bicep.

Folderinfra/
LanguageBicep · Azure CLI
APIAzure Resource Manager
AuthAzure RBAC (Contributor on RG)
Deploys viadeploy-infra.yml
Data plane

The governance catalog

Sensitivity labels, DLP, data lifecycle & records, insider risk, communication compliance, DSPM, plus Data Map collections, glossary, classifications, sources & scans.

Folderdata-plane/ + scripts/
LanguageYAML rendered by PowerShell
APIPurview data-plane REST + S&C PowerShell
AuthApp-only identity (OIDC / cert in Key Vault)
Deploys viaper-solution deploy-<solution>.yml (5 surfaces); the rest apply locally
💡

Why the split? The account name is the DNS hostname of the data plane (<name>.purview.azure.com) and is treated as a fixed input. Collections, glossary, classifications, sources, and scans are invisible to ARM/Bicep — they live behind the Atlas v2 and Purview REST APIs, which is why the repo ships PowerShell reconcilers driven by YAML.

How it works

From pull request to deployed governance

A single CI/CD flow validates every change, then routes merges to the right plane by path. OIDC federated credentials give GitHub Actions short-lived tokens for both Azure Resource Manager and the Purview data plane — with no stored secret.

01

Pull request

You open a PR that changes one plane. Reviewers see the full diff of desired state.

02

Validate

validate.yml runs Bicep lint, yamllint, and PSScriptAnalyzer.

03

Merge to main

Path filters decide which deploy workflow fires next.

04

Deploy

infra/** → ARM. data-plane/** or scripts/** → Purview REST APIs.

Control-plane path

The deploy-infra workflow runs az deployment group create against infra/main.bicep, provisioning the Microsoft.Purview/accounts resource and its dependencies.

Data-plane path

Each per-solution deploy-<solution>.yml workflow authenticates via OIDC, then runs the one Deploy-*.ps1 reconciler it owns, rendering that YAML manifest into idempotent PUT/PATCH calls against the Purview data-plane APIs. Deletes are opt-in. Five surfaces have such a workflow today; the rest have no automated apply path yet and are applied by running their reconciler locally.

Use it yourself

From an empty copy to a deployable repo in six steps

This is a tenant-neutral template — every tenant-specific value is a documented placeholder (contoso, contoso.onmicrosoft.com). It will not deploy against a real tenant until you make it yours. The tenant onboarding guide has the exact commands.

  1. Get a copy

    Click Use this template on GitHub for a clean-history spin-off that can never push back, or git clone for a local workspace.

  2. Decouple it

    Run the @operator-kickoff agent to pick a local workspace or spin-off repo and install the no-push-back guard.

  3. Tailor it

    Run the @operator-tenant agent — it interviews you and writes your values into infra/parameters/lab.yaml and the identity-boundary statements.

  4. Review the diff

    Inspect what the agent produced, then commit it on a branch.

  5. Wire up identity

    Create the Microsoft Entra app + OIDC federated credential, set the GitHub Environment secrets, and set the OWNER_APPROVAL_LOGIN variable.

  6. Validate and deploy

    Run az bicep build and the Pester suite, then trigger deploy-infra and the per-solution deploy-<solution> workflows.

🔒

Never commit real identifiers or secrets. Real tenant IDs, subscription IDs, object IDs, and credentials belong in GitHub Environment secrets and are referenced by name. In source, always use the zero-GUID placeholder 00000000-0000-0000-0000-000000000000 and Microsoft's fictitious names (contoso, fabrikam, adatum).

Find your way around

Repository layout

Two planes, one folder each, plus the automation and docs that hold them together. The plane-to-workflow path filters depend on this separation — never put data-plane YAML under infra/, and never put Bicep under data-plane/.

.
├── .github/
│   ├── workflows/     # validate + deploy-infra + per-solution deploy-*
│   ├── instructions/  # path-scoped Copilot rules (Bicep, PowerShell, YAML…)
│   ├── prompts/       # reusable /-invoked task templates
│   └── agents/        # workspace-scoped Squad agents (personas)
├── infra/             # CONTROL PLANE — Bicep: Purview account + dependencies
│   ├── main.bicep
│   ├── main.bicepparam
│   ├── modules/
│   └── parameters/
├── data-plane/        # DATA PLANE — desired-state governance content
│   ├── information-protection/  # sensitivity labels + policies
│   ├── classifications/        # sensitive information type catalog
│   ├── dlp/  data-lifecycle/  records/  irm/  dspm/
│   ├── collections/  glossary/  data-sources/  scans/
│   └── purview-role-groups/  administrative-units/
├── scripts/           # PowerShell 7+ reconcilers, smoke tests, provisioning
│   ├── Connect-Purview.ps1
│   ├── Deploy-*.ps1   # idempotent reconciler per data-plane solution
│   └── modules/
├── docs/              # kickoff, onboarding, architecture, ADRs, runbooks
├── tests/             # Pester 5.x unit tests (no live tenant)
├── AGENTS.md
└── README.md

Non-negotiables

Design principles

Idempotent

Every apply script GETs current state, diffs, then PUT/PATCHes.

Declarative YAML

Human-reviewable manifests; a pull request equals a change review.

Least privilege

OIDC federated credentials from GitHub to Entra — no long-lived secrets.

Separation of concerns

Control-plane PRs never change catalog content, and vice versa.

Grounded in Microsoft Learn

References

Microsoft Learn is the authoritative source for every recommendation in this repo. The core pages behind the two-plane model: