Source: main (ad9f9ab) Excluded: live tenant exports, generated artifacts, and dev-only tooling.
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:
- Fork or import this repository into an Azure DevOps project.
- Review
templates/variables-tenant.ymland create a matching Azure DevOps Variable Group in your project (e.g.vg-astral-tenant). - Uncomment the variable group reference in the three pipeline YAMLs.
- Run
deploy/bootstrap-tenant.ps1to create the Azure AD app registration, assign Graph permissions, and configure the federated credential. - Create the Azure DevOps service connection using the app registration details from the bootstrap script.
- Import the three pipelines (
azure-pipelines.yml,azure-pipelines-review-sync.yml,azure-pipelines-restore.yml) into Azure DevOps. - Run
deploy/validate-deployment.ymlto verify connectivity and permissions. - Set
AUTO_REMEDIATE_RESTORE_PIPELINE_IDin your variable group after the restore pipeline is imported.
See 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: hourly backup/export pipeline with rolling PR management.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:
- Export Intune and Entra configuration into
tenant-state/. - Generate Markdown/CSV reports in
tenant-state/reports/. - Filter known non-actionable drift noise before commit.
- Commit workload drift to
drift/intuneanddrift/entra. - Create or update one rolling PR per workload into
main. - Refresh the PR description with deterministic change/risk summary and optional Azure OpenAI narrative.
- Apply reviewer
/rejector/acceptdecisions and queue restore when needed.
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 RegistrationsandEnterprise Applicationsis 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.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, and remediation helpers.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 hourly on main.
For Intune it:
- Prepares
drift/intunefrommain. - Chooses light vs full mode from the configured local timezone, with
forceFullRun=trueoverride. - Runs IntuneCD export.
- Reverts partial Settings Catalog exports with
scripts/filter_intune_partial_settings_noise.py. - Resolves assignment group names from Graph when needed.
- Generates assignment and object inventory reports.
- Validates outputs with
scripts/validate_backup_outputs.py. - Commits drift and updates the rolling PR flow.
For Entra it:
- Prepares
drift/entrafrommain. - Chooses effective export scope per mode.
- Exports selected categories with
scripts/export_entra_baseline.py. - Resolves Conditional Access reference names with
scripts/resolve_ca_references.py. - Generates assignment, app, and object inventory reports.
- Validates outputs with
scripts/validate_backup_outputs.py. - Reverts enrichment-only JSON churn with
scripts/filter_entra_enrichment_noise.py. - 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
/rejectand/acceptdecisions withscripts/apply_reviewer_rejections.py - refresh the automated PR summary
- queue restore after merged PRs that contained reviewer
/rejectdecisions usingscripts/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 baselineselective: 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: hourly,
0 * * * *, onmain - Review sync schedule: every 20 minutes,
*/20 * * * *, onmain - Full mode: configured full-run hour (default 00:00) or manual queue with
forceFullRun=true - Light mode: every other scheduled hour
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/intunedrift/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_STRATEGYand defaults torebase
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 Neededreview threads whenREQUIRE_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:
- Reject and abandon a whole rolling PR. The next run can detect the matching rejected snapshot and queue restore automatically.
- 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.
- Queue
azure-pipelines-restore.ymlmanually for full or selective historical rollback.
Key Variables
Core repo and branch settings:
BASELINE_BRANCHDRIFT_BRANCH_INTUNEDRIFT_BRANCH_ENTRAROLLING_PR_TITLE_INTUNEROLLING_PR_TITLE_ENTRABACKUP_FOLDERINTUNE_BACKUP_SUBDIRENTRA_BACKUP_SUBDIRREPORTS_SUBDIR
Workload toggles:
ENABLE_WORKLOAD_INTUNEENABLE_WORKLOAD_ENTRAENABLE_ENTRA_CONDITIONAL_ACCESS
Intune behavior:
INTUNECD_VERSIONEXCLUDE_SCRIPT_BACKUPINTUNE_EXCLUDE_CSVSPLIT_DOCUMENTATION
Entra behavior:
ENTRA_INCLUDE_NAMED_LOCATIONSENTRA_INCLUDE_AUTHENTICATION_STRENGTHSENTRA_INCLUDE_CONDITIONAL_ACCESSENTRA_INCLUDE_APP_REGISTRATIONSENTRA_INCLUDE_ENTERPRISE_APPSENTRA_ENTERPRISE_APP_WORKERS
PR and reviewer automation:
ENABLE_PR_REVIEW_SUMMARYENABLE_PR_REVIEWER_DECISIONSROLLING_PR_DELAY_REVIEWER_NOTIFICATIONSROLLING_PR_MERGE_STRATEGYREQUIRE_CHANGE_TICKETSCHANGE_TICKET_REGEXDEBUG_CHANGE_TICKET_THREADS
Auto-remediation:
AUTO_REMEDIATE_ON_PR_REJECTIONAUTO_REMEDIATE_AFTER_MERGEAUTO_REMEDIATE_AFTER_MERGE_LOOKBACK_HOURSAUTO_REMEDIATE_RESTORE_PIPELINE_IDAUTO_REMEDIATE_DRY_RUNAUTO_REMEDIATE_UPDATE_ASSIGNMENTSAUTO_REMEDIATE_REMOVE_OBJECTSAUTO_REMEDIATE_MAX_WORKERSAUTO_REMEDIATE_EXCLUDE_CSV
Azure OpenAI integration:
ENABLE_PR_AI_SUMMARYAZURE_OPENAI_ENDPOINTAZURE_OPENAI_DEPLOYMENTAZURE_OPENAI_API_KEYAZURE_OPENAI_API_VERSIONPR_AI_PAYLOAD_MAX_BYTESPR_AI_MAX_TOKENSPR_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 buildsQueue 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.AllDeviceManagementApps.Read.AllDeviceManagementConfiguration.Read.AllDeviceManagementManagedDevices.Read.AllDeviceManagementRBAC.Read.AllDeviceManagementScripts.Read.AllDeviceManagementServiceConfig.Read.AllGroup.Read.AllPolicy.Read.AllPolicy.Read.ConditionalAccessPolicy.Read.DeviceConfigurationUser.Read.All
Additional read permissions used by the current Entra scope:
Application.Read.AllRoleManagement.Read.DirectoryorDirectory.Read.Allfor richer name resolutionAuditLog.Read.Allfor best-effort Entra drift author attribution
Restore pipeline write permissions:
DeviceManagementApps.ReadWrite.AllDeviceManagementConfiguration.ReadWrite.AllDeviceManagementManagedDevices.ReadWrite.AllDeviceManagementRBAC.ReadWrite.AllDeviceManagementScripts.ReadWrite.AllDeviceManagementServiceConfig.ReadWrite.AllGroup.Read.All
Additional restore permission when includeEntraUpdate=true:
Policy.Read.AllPolicy.ReadWrite.ConditionalAccess
Outputs
Intune outputs:
- JSON backup under
tenant-state/intune/** tenant-state/reports/intune/policy-assignments.mdtenant-state/reports/intune/policy-assignments.csvtenant-state/reports/intune/object-inventory-all.csvtenant-state/reports/intune/Object Inventory/*-inventory.csv
Entra outputs:
- JSON backup under
tenant-state/entra/** tenant-state/reports/entra/policy-assignments.mdtenant-state/reports/entra/policy-assignments.csvtenant-state/reports/entra/apps-inventory.csvtenant-state/reports/entra/object-inventory-all.csvtenant-state/reports/entra/Object Inventory/*-inventory.csv
Full-run documentation artifacts:
prod-as-built-split-markdownprod-as-built-split-htmlprod-as-built-split-pdf
Local Script Usage
Generate Entra export locally:
python3 ./scripts/export_entra_baseline.py \
--root ./tenant-state/entra \
--token "$GRAPH_TOKEN" \
--enterprise-app-workers 8
Resolve Conditional Access references:
python3 ./scripts/resolve_ca_references.py \
--root ./tenant-state/entra \
--token "$GRAPH_TOKEN"
Generate Intune assignment report:
python3 ./scripts/generate_assignment_report.py \
--root ./tenant-state/intune \
--output-dir ./tenant-state/reports/intune
Generate Entra assignment report:
python3 ./scripts/generate_assignment_report.py \
--root ./tenant-state/entra \
--output-dir ./tenant-state/reports/entra
Generate Entra apps inventory:
python3 ./scripts/generate_app_inventory_report.py \
--root ./tenant-state/entra \
--output-dir ./tenant-state/reports/entra
Generate workload object inventories:
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:
python3 ./scripts/validate_backup_outputs.py \
--workload intune \
--mode light \
--root ./tenant-state/intune \
--reports-root ./tenant-state/reports/intune
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