Source: main (497baf0) Excluded: live tenant exports, generated artifacts, and dev-only tooling.
468 lines
16 KiB
Markdown
468 lines
16 KiB
Markdown
# Intune / Entra Drift Backup
|
|
|
|
This repository keeps Git-tracked snapshots of Microsoft Intune and selected Entra ID configuration, generates review reports, and drives a rolling pull-request workflow for post-change review and remediation.
|
|
|
|
> **Product name:** ASTRAL (Admin Security Through Review, Automation & Least-privilege)
|
|
|
|
## Getting Started
|
|
|
|
This repository is designed to be forked or downloaded into your own Azure DevOps organization. Each tenant gets its own project and pipeline instance.
|
|
|
|
Quick start:
|
|
|
|
1. Fork or import this repository into an Azure DevOps project.
|
|
2. Review `templates/variables-tenant.yml` and create a matching Azure DevOps Variable Group in your project (e.g. `vg-astral-tenant`).
|
|
3. Uncomment the variable group reference in the three pipeline YAMLs.
|
|
4. Run `deploy/provision-change-probe.ps1` to create the Azure AD app registration, assign Graph permissions, configure the federated credential, and optionally provision the event-driven change probe (Azure Function App).
|
|
5. Create the Azure DevOps service connection using the app registration details from the bootstrap script.
|
|
6. Import the three pipelines (`azure-pipelines.yml`, `azure-pipelines-review-sync.yml`, `azure-pipelines-restore.yml`) into Azure DevOps.
|
|
7. Run `deploy/validate-deployment.yml` to verify connectivity and permissions.
|
|
8. Set `AUTO_REMEDIATE_RESTORE_PIPELINE_ID` in your variable group after the restore pipeline is imported.
|
|
|
|
See [`deploy/onboarding-runbook.md`](deploy/onboarding-runbook.md) for the full step-by-step guide.
|
|
|
|
## What The Repository Does
|
|
|
|
The implementation is centered on three Azure DevOps pipelines:
|
|
|
|
- `azure-pipelines.yml`: backup/export pipeline with rolling PR management. Runs daily at 02:00 to generate a full tenant snapshot, reports, and documentation artifacts, and is also triggered on-demand by the event-driven change probe.
|
|
- `azure-pipelines-review-sync.yml`: 20-minute reviewer-decision sync and post-merge remediation queue.
|
|
- `azure-pipelines-restore.yml`: manual or auto-queued restore pipeline for approved baseline rollback.
|
|
|
|
The main workflow is:
|
|
|
|
1. Export Intune and Entra configuration into `tenant-state/`.
|
|
2. Generate Markdown/CSV reports in `tenant-state/reports/`.
|
|
3. Filter known non-actionable drift noise before commit.
|
|
4. Commit workload drift to `drift/intune` and `drift/entra`.
|
|
5. Create or update one rolling PR per workload into `main`.
|
|
6. Refresh the PR description with deterministic change/risk summary and optional Azure OpenAI narrative.
|
|
7. Apply reviewer `/reject` or `/accept` decisions and queue restore when needed.
|
|
|
|
An **event-driven change probe** monitors Intune and Entra audit logs and triggers the backup pipeline when actual changes are detected, replacing the previous hourly polling model with a responsive event-driven approach.
|
|
|
|
This is an ex-post change-management model: admins can change settings in the Microsoft admin portals, and the repo turns those changes into auditable Git drift with a review and rollback path.
|
|
|
|
## Current Baseline Coverage
|
|
|
|
Intune currently tracks:
|
|
|
|
- App Configuration
|
|
- App Protection
|
|
- Apple Push Notification
|
|
- Apple VPP Tokens
|
|
- Applications
|
|
- Compliance Policies
|
|
- Device Configurations
|
|
- Device Management Settings
|
|
- Enrollment Configurations
|
|
- Enrollment Profiles
|
|
- Filters
|
|
- Scope Tags
|
|
- Scripts
|
|
- Settings Catalog
|
|
|
|
Entra currently tracks:
|
|
|
|
- Named Locations
|
|
- Authentication Strengths
|
|
- Conditional Access
|
|
- App Registrations
|
|
- Enterprise Applications
|
|
|
|
Current scope behavior:
|
|
|
|
- Named Locations, Authentication Strengths, and Conditional Access run on hourly light runs and midnight full runs.
|
|
- App Registrations and Enterprise Applications are enabled in the pipeline but exported only on full runs.
|
|
- During light runs, the previous drift-branch snapshot of `App Registrations` and `Enterprise Applications` is preserved to avoid churn and heavy export cost.
|
|
|
|
## Repository Layout
|
|
|
|
- `README.md`: operational overview.
|
|
- `azure-pipelines.yml`: backup/export, report generation, drift commit, rolling PR, and docs/artifact flow.
|
|
- `azure-pipelines-review-sync.yml`: reviewer decision sync and post-merge remediation helper.
|
|
- `azure-pipelines-restore.yml`: baseline restore pipeline with full or selective scope.
|
|
- `infra/change-probe/`: Azure Function App for event-driven change detection.
|
|
- `deploy/provision-change-probe.ps1`: unified provisioning script for the change probe infrastructure.
|
|
- `docs/m365-baseline-roadmap.md`: expansion roadmap beyond current workload scope.
|
|
- `docs/security-review-package.md`: implementation-focused security review package.
|
|
- `docs/security-review-questionnaire.md`: short-form security review answers.
|
|
- `scripts/`: export, reporting, PR automation, validation, remediation helpers, and change probe logic.
|
|
- `tests/`: focused unit coverage for the Python helpers.
|
|
- `tenant-state/intune`: committed Intune JSON export.
|
|
- `tenant-state/entra`: committed Entra JSON export.
|
|
- `tenant-state/reports/intune`: Intune CSV/Markdown reports.
|
|
- `tenant-state/reports/entra`: Entra CSV/Markdown reports.
|
|
- `prod-as-built.md`: generated as-built document source.
|
|
- `md2pdf/`: HTML/PDF styling and config for documentation publish.
|
|
|
|
## Pipeline Model
|
|
|
|
### Main Backup Pipeline
|
|
|
|
`azure-pipelines.yml` runs daily at 02:00 on `main` to generate a full tenant snapshot, reports, and documentation artifacts. It is also triggered on-demand by the change probe when drift is detected.
|
|
|
|
For Intune it:
|
|
|
|
1. Prepares `drift/intune` from `main`.
|
|
2. Chooses light vs full mode from the configured local timezone, with `forceFullRun=true` override.
|
|
3. Runs IntuneCD export.
|
|
4. Reverts partial Settings Catalog exports with `scripts/filter_intune_partial_settings_noise.py`.
|
|
5. Resolves assignment group names from Graph when needed.
|
|
6. Generates assignment and object inventory reports.
|
|
7. Validates outputs with `scripts/validate_backup_outputs.py`.
|
|
8. Commits drift and updates the rolling PR flow.
|
|
|
|
For Entra it:
|
|
|
|
1. Prepares `drift/entra` from `main`.
|
|
2. Chooses effective export scope per mode.
|
|
3. Exports selected categories with `scripts/export_entra_baseline.py`.
|
|
4. Resolves Conditional Access reference names with `scripts/resolve_ca_references.py`.
|
|
5. Generates assignment, app, and object inventory reports.
|
|
6. Validates outputs with `scripts/validate_backup_outputs.py`.
|
|
7. Reverts enrichment-only JSON churn with `scripts/filter_entra_enrichment_noise.py`.
|
|
8. Commits drift with `scripts/commit_entra_drift.py`.
|
|
|
|
### Review Sync Pipeline
|
|
|
|
`azure-pipelines-review-sync.yml` runs every 20 minutes on `main` and exists to shorten the reviewer feedback loop.
|
|
|
|
Per workload it can:
|
|
|
|
- apply reviewer `/reject` and `/accept` decisions with `scripts/apply_reviewer_rejections.py`
|
|
- refresh the automated PR summary
|
|
- queue restore after merged PRs that contained reviewer `/reject` decisions using `scripts/queue_post_merge_restore.py`
|
|
|
|
### Restore Pipeline
|
|
|
|
`azure-pipelines-restore.yml` restores from approved baseline (`main` by default) or from a historical branch, tag, or commit.
|
|
|
|
Supported restore modes:
|
|
|
|
- `full`: restore the full committed Intune baseline
|
|
- `selective`: restore only selected file paths
|
|
|
|
It also supports optional Entra update when restore automation is triggered for Entra review outcomes.
|
|
|
|
## Schedule And Run Modes
|
|
|
|
- Main backup schedule: daily at 02:00, `0 2 * * *`, on `main` (full snapshot, reports, and docs)
|
|
- Change probe trigger: event-driven, on-demand via Azure Function App
|
|
- Review sync schedule: every 20 minutes, `*/20 * * * *`, on `main`
|
|
- Full mode: configured full-run hour (default 00:00) or manual queue with `forceFullRun=true`
|
|
- Light mode: all probe-triggered runs except the daily full run
|
|
|
|
### Change Probe (Event-Driven Backup Trigger)
|
|
|
|
Because Microsoft Graph change notifications and delta queries do not support Intune device management or Conditional Access resources, an audit-log polling architecture is used:
|
|
|
|
- **`probe_timer`** (5-minute timer trigger): polls Intune and Entra audit logs via Microsoft Graph, evaluates a debouncer state machine (idle → armed → cooldown), and emits a queue message when the quiet window elapses.
|
|
- **`queue_consumer`** (queue trigger): dequeues messages and calls the Azure DevOps REST API to queue the backup pipeline.
|
|
- **Debouncer**: 15-minute quiet window + 30-minute cooldown prevents backup storms during bulk changes.
|
|
- **State**: stored in Azure Table Storage (`ProbeState` table).
|
|
- **Provisioning**: `deploy/provision-change-probe.ps1` creates the Entra app, grants admin consent, provisions Resource Group / Storage Account / Function App, and configures app settings.
|
|
|
|
Full mode adds:
|
|
|
|
- full Entra scope, including App Registrations and Enterprise Applications
|
|
- Intune split-documentation generation
|
|
- HTML/PDF artifact generation when browser dependencies are available
|
|
- optional tagging and documentation publish steps
|
|
|
|
## Branch And PR Model
|
|
|
|
- Baseline branch: `main`
|
|
- Drift branches:
|
|
- `drift/intune`
|
|
- `drift/entra`
|
|
|
|
Each workload keeps one rolling PR open to `main`.
|
|
|
|
Key behavior:
|
|
|
|
- reports are generated in `tenant-state/reports/*` but excluded from rolling drift commits and PR diffs
|
|
- rolling PRs can be created as draft first, then published after automated summary generation when `ROLLING_PR_DELAY_REVIEWER_NOTIFICATIONS=true`
|
|
- merge strategy for the rolling PR is controlled by `ROLLING_PR_MERGE_STRATEGY` and defaults to `rebase`
|
|
|
|
## Review, Tickets, And Remediation
|
|
|
|
The PR automation currently supports:
|
|
|
|
- deterministic operation counts and risk assessment
|
|
- rename-aware semantic comparison
|
|
- stable change fingerprinting for idempotent summary refresh
|
|
- optional Azure OpenAI reviewer narrative
|
|
- optional per-file `Change Needed` review threads when `REQUIRE_CHANGE_TICKETS=true`
|
|
|
|
Reviewer thread commands:
|
|
|
|
- `/reject`: remove that file-level drift from the rolling PR by resetting it to baseline
|
|
- `/accept`: keep that file in PR scope
|
|
|
|
Supported remediation paths:
|
|
|
|
1. Reject and abandon a whole rolling PR.
|
|
The next run can detect the matching rejected snapshot and queue restore automatically.
|
|
2. Reject selected files in ticket threads, then merge the accepted remainder.
|
|
The review-sync pipeline can queue restore after merge so the tenant is reconciled to the merged baseline.
|
|
3. Queue `azure-pipelines-restore.yml` manually for full or selective historical rollback.
|
|
|
|
## Key Variables
|
|
|
|
Core repo and branch settings:
|
|
|
|
- `BASELINE_BRANCH`
|
|
- `DRIFT_BRANCH_INTUNE`
|
|
- `DRIFT_BRANCH_ENTRA`
|
|
- `ROLLING_PR_TITLE_INTUNE`
|
|
- `ROLLING_PR_TITLE_ENTRA`
|
|
- `BACKUP_FOLDER`
|
|
- `INTUNE_BACKUP_SUBDIR`
|
|
- `ENTRA_BACKUP_SUBDIR`
|
|
- `REPORTS_SUBDIR`
|
|
|
|
Workload toggles:
|
|
|
|
- `ENABLE_WORKLOAD_INTUNE`
|
|
- `ENABLE_WORKLOAD_ENTRA`
|
|
- `ENABLE_ENTRA_CONDITIONAL_ACCESS`
|
|
|
|
Intune behavior:
|
|
|
|
- `INTUNECD_VERSION`
|
|
- `EXCLUDE_SCRIPT_BACKUP`
|
|
- `INTUNE_EXCLUDE_CSV`
|
|
- `SPLIT_DOCUMENTATION`
|
|
|
|
Entra behavior:
|
|
|
|
- `ENTRA_INCLUDE_NAMED_LOCATIONS`
|
|
- `ENTRA_INCLUDE_AUTHENTICATION_STRENGTHS`
|
|
- `ENTRA_INCLUDE_CONDITIONAL_ACCESS`
|
|
- `ENTRA_INCLUDE_APP_REGISTRATIONS`
|
|
- `ENTRA_INCLUDE_ENTERPRISE_APPS`
|
|
- `ENTRA_ENTERPRISE_APP_WORKERS`
|
|
|
|
PR and reviewer automation:
|
|
|
|
- `ENABLE_PR_REVIEW_SUMMARY`
|
|
- `ENABLE_PR_REVIEWER_DECISIONS`
|
|
- `ROLLING_PR_DELAY_REVIEWER_NOTIFICATIONS`
|
|
- `ROLLING_PR_MERGE_STRATEGY`
|
|
- `REQUIRE_CHANGE_TICKETS`
|
|
- `CHANGE_TICKET_REGEX`
|
|
- `DEBUG_CHANGE_TICKET_THREADS`
|
|
|
|
Auto-remediation:
|
|
|
|
- `AUTO_REMEDIATE_ON_PR_REJECTION`
|
|
- `AUTO_REMEDIATE_AFTER_MERGE`
|
|
- `AUTO_REMEDIATE_AFTER_MERGE_LOOKBACK_HOURS`
|
|
- `AUTO_REMEDIATE_RESTORE_PIPELINE_ID`
|
|
- `AUTO_REMEDIATE_DRY_RUN`
|
|
- `AUTO_REMEDIATE_UPDATE_ASSIGNMENTS`
|
|
- `AUTO_REMEDIATE_REMOVE_OBJECTS`
|
|
- `AUTO_REMEDIATE_MAX_WORKERS`
|
|
- `AUTO_REMEDIATE_EXCLUDE_CSV`
|
|
|
|
Change probe settings:
|
|
|
|
- `PROBE_APP_ID`
|
|
- `PROBE_APP_SECRET`
|
|
- `PROBE_QUIET_WINDOW_MINUTES` (default: 15)
|
|
- `PROBE_COOLDOWN_MINUTES` (default: 30)
|
|
- `GRAPH_TOKEN` (optional passthrough)
|
|
|
|
Azure OpenAI integration:
|
|
|
|
- `ENABLE_PR_AI_SUMMARY`
|
|
- `AZURE_OPENAI_ENDPOINT`
|
|
- `AZURE_OPENAI_DEPLOYMENT`
|
|
- `AZURE_OPENAI_API_KEY`
|
|
- `AZURE_OPENAI_API_VERSION`
|
|
- `PR_AI_PAYLOAD_MAX_BYTES`
|
|
- `PR_AI_MAX_TOKENS`
|
|
- `PR_AI_COMPACT_MAX_CHARS`
|
|
|
|
## Required Azure DevOps Permissions
|
|
|
|
The pipeline build identity should have repository permissions to:
|
|
|
|
- contribute
|
|
- create branch
|
|
- force push
|
|
- create and update pull requests
|
|
- create tag if tagging is enabled
|
|
|
|
For auto-queued restore, the same identity also needs on `azure-pipelines-restore.yml`:
|
|
|
|
- `View builds`
|
|
- `Queue builds`
|
|
- pipeline authorization if explicit pipeline permissions are enforced
|
|
|
|
Also enable script access to `System.AccessToken`.
|
|
|
|
## Required Microsoft Graph Application Permissions
|
|
|
|
Baseline read permissions used by the current implementation:
|
|
|
|
- `Device.Read.All`
|
|
- `DeviceManagementApps.Read.All`
|
|
- `DeviceManagementConfiguration.Read.All`
|
|
- `DeviceManagementManagedDevices.Read.All`
|
|
- `DeviceManagementRBAC.Read.All`
|
|
- `DeviceManagementScripts.Read.All`
|
|
- `DeviceManagementServiceConfig.Read.All`
|
|
- `Group.Read.All`
|
|
- `Policy.Read.All`
|
|
- `Policy.Read.ConditionalAccess`
|
|
- `Policy.Read.DeviceConfiguration`
|
|
- `User.Read.All`
|
|
|
|
Additional read permissions used by the current Entra scope:
|
|
|
|
- `Application.Read.All`
|
|
- `RoleManagement.Read.Directory` or `Directory.Read.All` for richer name resolution
|
|
- `AuditLog.Read.All` for best-effort Entra drift author attribution
|
|
|
|
Restore pipeline write permissions:
|
|
|
|
- `DeviceManagementApps.ReadWrite.All`
|
|
- `DeviceManagementConfiguration.ReadWrite.All`
|
|
- `DeviceManagementManagedDevices.ReadWrite.All`
|
|
- `DeviceManagementRBAC.ReadWrite.All`
|
|
- `DeviceManagementScripts.ReadWrite.All`
|
|
- `DeviceManagementServiceConfig.ReadWrite.All`
|
|
- `Group.Read.All`
|
|
|
|
Additional restore permission when `includeEntraUpdate=true`:
|
|
|
|
- `Policy.Read.All`
|
|
- `Policy.ReadWrite.ConditionalAccess`
|
|
|
|
## Outputs
|
|
|
|
Intune outputs:
|
|
|
|
- JSON backup under `tenant-state/intune/**`
|
|
- `tenant-state/reports/intune/policy-assignments.md`
|
|
- `tenant-state/reports/intune/policy-assignments.csv`
|
|
- `tenant-state/reports/intune/object-inventory-all.csv`
|
|
- `tenant-state/reports/intune/Object Inventory/*-inventory.csv`
|
|
|
|
Entra outputs:
|
|
|
|
- JSON backup under `tenant-state/entra/**`
|
|
- `tenant-state/reports/entra/policy-assignments.md`
|
|
- `tenant-state/reports/entra/policy-assignments.csv`
|
|
- `tenant-state/reports/entra/apps-inventory.csv`
|
|
- `tenant-state/reports/entra/object-inventory-all.csv`
|
|
- `tenant-state/reports/entra/Object Inventory/*-inventory.csv`
|
|
|
|
Full-run documentation artifacts:
|
|
|
|
- `prod-as-built-split-markdown`
|
|
- `prod-as-built-split-html`
|
|
- `prod-as-built-split-pdf`
|
|
|
|
## Local Script Usage
|
|
|
|
Generate Entra export locally:
|
|
|
|
```bash
|
|
python3 ./scripts/export_entra_baseline.py \
|
|
--root ./tenant-state/entra \
|
|
--token "$GRAPH_TOKEN" \
|
|
--enterprise-app-workers 8
|
|
```
|
|
|
|
Resolve Conditional Access references:
|
|
|
|
```bash
|
|
python3 ./scripts/resolve_ca_references.py \
|
|
--root ./tenant-state/entra \
|
|
--token "$GRAPH_TOKEN"
|
|
```
|
|
|
|
Generate Intune assignment report:
|
|
|
|
```bash
|
|
python3 ./scripts/generate_assignment_report.py \
|
|
--root ./tenant-state/intune \
|
|
--output-dir ./tenant-state/reports/intune
|
|
```
|
|
|
|
Generate Entra assignment report:
|
|
|
|
```bash
|
|
python3 ./scripts/generate_assignment_report.py \
|
|
--root ./tenant-state/entra \
|
|
--output-dir ./tenant-state/reports/entra
|
|
```
|
|
|
|
Generate Entra apps inventory:
|
|
|
|
```bash
|
|
python3 ./scripts/generate_app_inventory_report.py \
|
|
--root ./tenant-state/entra \
|
|
--output-dir ./tenant-state/reports/entra
|
|
```
|
|
|
|
Generate workload object inventories:
|
|
|
|
```bash
|
|
python3 ./scripts/generate_object_inventory_reports.py \
|
|
--root ./tenant-state/intune \
|
|
--output-dir ./tenant-state/reports/intune
|
|
|
|
python3 ./scripts/generate_object_inventory_reports.py \
|
|
--root ./tenant-state/entra \
|
|
--output-dir ./tenant-state/reports/entra
|
|
```
|
|
|
|
Validate backup outputs:
|
|
|
|
```bash
|
|
python3 ./scripts/validate_backup_outputs.py \
|
|
--workload intune \
|
|
--mode light \
|
|
--root ./tenant-state/intune \
|
|
--reports-root ./tenant-state/reports/intune
|
|
```
|
|
|
|
Run the change probe locally:
|
|
|
|
```bash
|
|
python3 ./scripts/probe_tenant_changes.py \
|
|
--app-id "$PROBE_APP_ID" \
|
|
--app-secret "$PROBE_APP_SECRET" \
|
|
--tenant-id "$TENANT_ID" \
|
|
--state-file ./probe-state.json \
|
|
--output ./probe-result.json
|
|
```
|
|
|
|
Trigger the backup pipeline manually:
|
|
|
|
```bash
|
|
python3 ./scripts/trigger_backup_pipeline.py \
|
|
--organization cqre \
|
|
--project Intune \
|
|
--pipeline-id 1 \
|
|
--token "$ADO_TOKEN" \
|
|
--branch refs/heads/main
|
|
```
|
|
|
|
## Tests
|
|
|
|
The repository includes focused unit tests for:
|
|
|
|
- Entra export behavior
|
|
- backup output validation
|
|
- rolling PR creation/update logic
|
|
- PR summary generation
|
|
- reviewer rejection processing
|
|
- post-merge restore queueing
|
|
- Intune partial-export noise filtering
|
|
- Entra enrichment-noise filtering
|