d3e0769799
- Restructure launchers: Start-IntuneToolkit.ps1 moves to repo root; Start-HeadlessIntune.ps1 moves to Scripts/; TUI helper moves to Scripts/Private/ - Add AGENTS.md with project architecture, entry points, and security notes - Add CIS M365 baseline assets (CISM365-v7, M365-CIS-Rapid) and reporting scripts - Add Python reporting utilities (Export-SettingsReport, Export-AssignmentReport, Export-ObjectInventoryReport) and CA wizard helpers - Update Deploy-IntuneBaseline.ps1 with Merge conflict resolution, ReportPath, and optimized group loading - Update Initialize-IntuneAuth.ps1 with -RotateSecret and configurable secret expiry - Update Extensions for Settings Catalog definition auto-export - Update README with v4.1.0, new entry points and script catalog - Bump VERSION to 4.1.0 - Harden .gitignore against .DS_Store, __pycache__, .venv-pdf/, local exports, Settings.json and IntuneManagement.log
171 lines
8.0 KiB
Markdown
171 lines
8.0 KiB
Markdown
# macOS Intune Management
|
|
|
|
Cross-platform, headless Intune policy export/import with PowerShell.
|
|
|
|
**Current version:** `4.1.0` — see [`CHANGELOG_macOS_IntuneToolkit.md`](CHANGELOG_macOS_IntuneToolkit.md) for recent changes.
|
|
|
|
This repository is now CLI-first. The old WPF application surface has been removed from the repo. The supported workflow is:
|
|
|
|
1. export policies from a source tenant
|
|
2. store the exported JSON and migration table
|
|
3. import into a target tenant with app-only or browser authentication
|
|
|
|
## Quick start
|
|
|
|
The easiest way to get started is the unified launcher. It provides a single terminal UI for every tool and remembers your tenants.
|
|
|
|
```powershell
|
|
pwsh ./Start-IntuneToolkit.ps1
|
|
```
|
|
|
|
If `fzf` is installed you get an interactive picker; otherwise you get a numbered menu. You can also pass a tenant directly:
|
|
|
|
```powershell
|
|
pwsh ./Start-IntuneToolkit.ps1 -TenantId "<tenant-id>"
|
|
```
|
|
|
|
## Entry points
|
|
|
|
* [Start-IntuneToolkit.ps1](/Users/avedelphina/Local/IntuneManagement/Start-IntuneToolkit.ps1) — unified launcher (recommended)
|
|
* [Scripts/Start-HeadlessIntune.ps1](/Users/avedelphina/Local/IntuneManagement/Scripts/Start-HeadlessIntune.ps1) — single action wrapper with optional TUI
|
|
* [Scripts/Export-Policies.ps1](/Users/avedelphina/Local/IntuneManagement/Scripts/Export-Policies.ps1)
|
|
* [Scripts/Import-Policies.ps1](/Users/avedelphina/Local/IntuneManagement/Scripts/Import-Policies.ps1)
|
|
* [Scripts/Initialize-IntuneAuth.ps1](/Users/avedelphina/Local/IntuneManagement/Scripts/Initialize-IntuneAuth.ps1) — one-time Entra app + secret + Keychain setup
|
|
* [Scripts/Export-SettingsReport.py](/Users/avedelphina/Local/IntuneManagement/Scripts/Export-SettingsReport.py) — generate a flat CSV of policy settings/values
|
|
* [Headless/IntuneManagement.Headless.psd1](/Users/avedelphina/Local/IntuneManagement/Headless/IntuneManagement.Headless.psd1)
|
|
|
|
## Runtime
|
|
|
|
* `pwsh` 7+
|
|
* Microsoft Graph app registration
|
|
* App-only auth with client secret or certificate, or browser auth with a public client redirect URI
|
|
* `fzf` (optional) — for the best interactive menu experience in `Start-IntuneToolkit.ps1`. Falls back to numbered menus if not installed.
|
|
* macOS: `brew install fzf`
|
|
* Linux: `sudo apt install fzf` (or `dnf` / `pacman`)
|
|
* Windows: `winget install junegunn.fzf` (or `choco install fzf`)
|
|
|
|
## Default object types
|
|
|
|
The default headless policy scope is:
|
|
|
|
* `DeviceConfiguration`
|
|
* `SettingsCatalog`
|
|
* `AdministrativeTemplates`
|
|
* `CompliancePolicies`
|
|
* `EndpointSecurity`
|
|
* `PolicySets`
|
|
|
|
You can override that list with `-ObjectTypes`.
|
|
|
|
## First-time setup
|
|
|
|
If you don't already have an Entra app registration, run the auth initializer. It creates the app, grants admin consent, and stores the secret in the macOS Keychain (or Windows Credential Manager).
|
|
|
|
```powershell
|
|
pwsh ./Scripts/Initialize-IntuneAuth.ps1
|
|
```
|
|
|
|
## Export
|
|
|
|
```powershell
|
|
pwsh ./Scripts/Export-Policies.ps1 `
|
|
-TenantId "<source-tenant-id>" `
|
|
-AppId "<app-id>" `
|
|
-Secret "<client-secret>" `
|
|
-ExportPath "/tmp/intune-export" `
|
|
-IncludeAssignments
|
|
```
|
|
|
|
## Export with browser auth
|
|
|
|
```powershell
|
|
pwsh ./Scripts/Export-Policies.ps1 `
|
|
-TenantId "<source-tenant-id>" `
|
|
-AuthMode Browser `
|
|
-ExportPath "/tmp/intune-export"
|
|
```
|
|
|
|
## Import
|
|
|
|
```powershell
|
|
pwsh ./Scripts/Import-Policies.ps1 `
|
|
-TenantId "<target-tenant-id>" `
|
|
-AppId "<app-id>" `
|
|
-Secret "<client-secret>" `
|
|
-ImportPath "/tmp/intune-export/SourceTenantName" `
|
|
-ImportType alwaysImport `
|
|
-IncludeAssignments `
|
|
-IncludeScopeTags `
|
|
-ReplaceDependencyIds
|
|
```
|
|
|
|
## Import with browser auth
|
|
|
|
```powershell
|
|
pwsh ./Scripts/Import-Policies.ps1 `
|
|
-TenantId "<target-tenant-id>" `
|
|
-AuthMode Browser `
|
|
-ImportPath "/tmp/intune-export/SourceTenantName"
|
|
```
|
|
|
|
## Single action entry point
|
|
|
|
```powershell
|
|
pwsh ./Scripts/Start-HeadlessIntune.ps1 `
|
|
-Action Export `
|
|
-TenantId "<source-tenant-id>" `
|
|
-AppId "<app-id>" `
|
|
-Secret "<client-secret>" `
|
|
-ExportPath "/tmp/intune-export"
|
|
```
|
|
|
|
```powershell
|
|
pwsh ./Scripts/Start-HeadlessIntune.ps1 `
|
|
-Action Import `
|
|
-TenantId "<target-tenant-id>" `
|
|
-AppId "<app-id>" `
|
|
-Secret "<client-secret>" `
|
|
-ImportPath "/tmp/intune-export/SourceTenantName" `
|
|
-ImportType alwaysImport
|
|
```
|
|
|
|
```powershell
|
|
pwsh ./Scripts/Start-HeadlessIntune.ps1 `
|
|
-Action Export `
|
|
-TenantId "<source-tenant-id>" `
|
|
-AuthMode Browser `
|
|
-RedirectUri "http://localhost" `
|
|
-ExportPath "/tmp/intune-export"
|
|
```
|
|
|
|
## Additional toolkit scripts
|
|
|
|
* **Baseline deployment** — [`Deploy-IntuneBaseline.ps1`](Scripts/Deploy-IntuneBaseline.ps1) deploys a YAML manifest of policies + assignments to a tenant, with dry-run support. [`ConvertTo-IntuneBaseline.ps1`](Scripts/ConvertTo-IntuneBaseline.ps1) turns an existing export folder into a baseline skeleton.
|
|
* **CIS M365 baseline** — [`Deploy-CISM365Baseline.ps1`](Scripts/Deploy-CISM365Baseline.ps1) applies the CIS Microsoft 365 v7 benchmark to a tenant. See [`Baselines/M365-CIS-Rapid/`](Baselines/M365-CIS-Rapid/) for a config-driven rapid baseline.
|
|
* **Bulk assignments** — [`Bulk-AssignmentManager.ps1`](Scripts/Bulk-AssignmentManager.ps1) adds or removes assignments for any policy type using the bulk `/assign` endpoint. [`Bulk-AppAssignment.ps1`](Scripts/Bulk-AppAssignment.ps1) does the same for applications.
|
|
* **Backup / restore assignments** — [`Backup-Restore-Assignments.ps1`](Scripts/Backup-Restore-Assignments.ps1) saves assignments to JSON and can restore them with cross-tenant group name resolution.
|
|
* **Bulk rename** — [`Bulk-RenamePolicies.ps1`](Scripts/Bulk-RenamePolicies.ps1) performs search/replace or prefix mutations across policy names and descriptions.
|
|
* **Device operations** — [`Bulk-DeviceOperations.ps1`](Scripts/Bulk-DeviceOperations.ps1) supports delete, retire, wipe, lock, and sync with `-WhatIf` safeguards.
|
|
* **Assignment documentation** — [`Export-AssignmentsToCsv.ps1`](Scripts/Export-AssignmentsToCsv.ps1) exports assignments to CSV and Markdown.
|
|
* **Reporting utilities** — [`Export-SettingsReport.py`](Scripts/Export-SettingsReport.py), [`Export-AssignmentReport.py`](Scripts/Export-AssignmentReport.py), and [`Export-ObjectInventoryReport.py`](Scripts/Export-ObjectInventoryReport.py) generate CSV/Markdown reports from local exports.
|
|
* **Baseline batch runner** — [`Invoke-BaselineBatch.ps1`](Scripts/Invoke-BaselineBatch.ps1) run multiple baseline manifests in one pass.
|
|
* **Conditional Access wizard** — [`Start-CAWizard.ps1`](Scripts/Start-CAWizard.ps1) / [`ca-wizard.py`](Scripts/ca-wizard.py) generate Conditional Access baseline skeletons.
|
|
|
|
## Notes
|
|
|
|
* Export writes a migration table used during cross-tenant import.
|
|
* Import can translate dependency IDs and recreate missing assignment groups.
|
|
* This repo intentionally does not preserve the old Windows UI launch flow.
|
|
* Browser auth uses the system browser and a loopback redirect.
|
|
* If you omit `-AppId` with `-AuthMode Browser`, the CLI defaults to the Microsoft Graph PowerShell public client app id `14d82eec-204b-4c2f-b7e8-296a70dab67e`.
|
|
* If your own app registration does not allow loopback redirects, pass `-AppId` and `-RedirectUri "http://localhost"` and configure the same redirect URI in Entra ID.
|
|
|
|
## Accountability & PIM caveats
|
|
|
|
By default `Initialize-IntuneAuth.ps1` creates an **app-only** registration. Every Graph call is authenticated as the service principal, not as an individual user.
|
|
|
|
* **Audit logs** show the app's display name (e.g., `IntuneManagement-tomas.kracmar@cqre.net`), not the admin's UPN. The initializer now automatically names the app after the **authenticated Entra user** to improve traceability.
|
|
* **PIM is not enforced** for app-only secrets. The service principal has standing permissions, so write operations can occur outside an elevated PIM window.
|
|
* If you need strict PIM compliance, use **delegated authentication** (`-AuthMode Browser` or `-AuthMode DeviceCode`) so calls are made in the signed-in user's context. Note that `DeviceCode` may be blocked by Conditional Access policies.
|
|
* To fully remove a tenant's local credentials **and** the Entra app registration, use menu item **15** in the TUI or run `./Scripts/Initialize-IntuneAuth.ps1 -TenantId "<id>" -DeleteApp`.
|