|
| 1 | +# CIPP-API Project Conventions |
| 2 | + |
| 3 | +## Platform |
| 4 | + |
| 5 | +- **Azure Functions** app running **PowerShell 7.4** |
| 6 | +- Uses **Durable Functions** for orchestration (fan-out/fan-in, long-running workflows) |
| 7 | +- All persistent data stored in **Azure Table Storage** (no SQL) |
| 8 | +- Telemetry via **Application Insights** (optional) |
| 9 | + |
| 10 | +## Project layout |
| 11 | + |
| 12 | +``` |
| 13 | +├── Modules/ # All PowerShell modules — bundled locally, not external |
| 14 | +│ ├── CIPPCore/ # Main module (~300+ exported functions) |
| 15 | +│ │ ├── Public/ # Exported functions (auto-loaded recursively) |
| 16 | +│ │ ├── Private/ # Internal-only functions |
| 17 | +│ │ └── lib/ # Binary dependencies (Cronos.dll, etc.) |
| 18 | +│ ├── CippEntrypoints/ # HTTP/trigger router functions |
| 19 | +│ ├── CippExtensions/ # Third-party integrations (Hudu, Halo, NinjaOne, etc.) |
| 20 | +│ ├── AzBobbyTables/ # Azure Table Storage helper module |
| 21 | +│ ├── DNSHealth/ # DNS validation |
| 22 | +│ ├── MicrosoftTeams/ # Teams API helpers |
| 23 | +│ └── AzureFunctions.PowerShell.Durable.SDK/ |
| 24 | +├── CIPPHttpTrigger/ # Single HTTP trigger → routes all API requests |
| 25 | +├── CIPPOrchestrator/ # Durable orchestration trigger |
| 26 | +├── CIPPActivityFunction/ # Durable activity trigger (parallelizable work) |
| 27 | +├── CIPPQueueTrigger/ # Queue-based async processing |
| 28 | +├── CIPPTimer/ # Timer trigger (runs every 15 min) |
| 29 | +├── Config/ # JSON templates (CA, Intune, Transport Rules, BPA) |
| 30 | +├── Tests/ # Pester tests |
| 31 | +├── profile.ps1 # Module loading at startup |
| 32 | +└── host.json # Azure Functions runtime config |
| 33 | +``` |
| 34 | + |
| 35 | +## Module loading |
| 36 | + |
| 37 | +Modules are **bundled in the repo**, not loaded from the PowerShell Gallery. `profile.ps1` imports them at startup in order: `CIPPCore` → `CippExtensions` → `AzBobbyTables`. The CIPPCore module auto-loads all functions under `Public/` recursively. No manifest changes are needed when adding new functions. |
| 38 | + |
| 39 | +## How HTTP requests work |
| 40 | + |
| 41 | +There is only **one** Azure Functions HTTP trigger (`CIPPHttpTrigger`). It routes all requests through `Receive-CippHttpTrigger` → `New-CippCoreRequest`, which: |
| 42 | + |
| 43 | +1. Reads the `CIPPEndpoint` parameter from the route |
| 44 | +2. Maps it to a function: `Invoke-{CIPPEndpoint}` |
| 45 | +3. Validates RBAC permissions via `Test-CIPPAccess` |
| 46 | +4. Checks feature flags |
| 47 | +5. Invokes the handler function |
| 48 | + |
| 49 | +**Only functions in `Modules/CIPPCore/Public/Entrypoints/HTTP Functions/` are callable by the frontend.** They are organized by domain: |
| 50 | + |
| 51 | +| Folder | Domain | |
| 52 | +|--------|--------| |
| 53 | +| `CIPP/` | Platform administration | |
| 54 | +| `Email-Exchange/` | Exchange Online | |
| 55 | +| `Endpoint/` | Intune / device management | |
| 56 | +| `Identity/` | Entra ID / users / groups | |
| 57 | +| `Security/` | Defender / Conditional Access | |
| 58 | +| `Teams-Sharepoint/` | Teams & SharePoint | |
| 59 | +| `Tenant/` | Tenant-level settings | |
| 60 | +| `Tools/` | Utility endpoints | |
| 61 | + |
| 62 | +### HTTP function naming |
| 63 | + |
| 64 | +- `Invoke-List*` — Read-only GET endpoints |
| 65 | +- `Invoke-Exec*` — Write/action endpoints |
| 66 | +- `Invoke-Add*` / `Invoke-Edit*` / `Invoke-Remove*` — CRUD variants |
| 67 | + |
| 68 | +Full naming rules, scaffolds, return conventions, and RBAC metadata are in `.github/instructions/http-entrypoints.instructions.md`, auto-loaded when editing HTTP Functions. |
| 69 | + |
| 70 | +## Durable Functions |
| 71 | + |
| 72 | +The app uses durable orchestration for anything that takes more than a few seconds: |
| 73 | + |
| 74 | +| Component | Purpose | |
| 75 | +|-----------|---------| |
| 76 | +| **Orchestrator** (`CIPPOrchestrator/`) | Coordinates multi-step workflows, fan-out/fan-in | |
| 77 | +| **Activity** (`CIPPActivityFunction/`) | Individual work units invoked by orchestrators in parallel | |
| 78 | +| **Queue** (`CIPPQueueTrigger/`) | Async task processing via `cippqueue` | |
| 79 | +| **Timer** (`CIPPTimer/`) | Runs every 15 minutes, triggers scheduled orchestrators | |
| 80 | + |
| 81 | +Orchestrator functions live in `Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/`. |
| 82 | +Activity triggers live in `Modules/CIPPCore/Public/Entrypoints/Activity Triggers/`. |
| 83 | +Timer functions live in `Modules/CIPPCore/Public/Entrypoints/Timer Functions/`. |
| 84 | + |
| 85 | +## Key helper functions |
| 86 | + |
| 87 | +Graph, Exchange, and Teams API helpers live in `Modules/CIPPCore/Public/GraphHelper/`. Key functions: `New-GraphGetRequest`, `New-GraphPOSTRequest`, `New-GraphBulkRequest`, `New-ExoRequest`, `New-ExoBulkRequest`, `New-TeamsRequest`. Full signatures and token details are in `.github/instructions/auth-model.instructions.md`. |
| 88 | + |
| 89 | +### Table Storage |
| 90 | + |
| 91 | +```powershell |
| 92 | +$Table = Get-CIPPTable -tablename 'TableName' |
| 93 | +$Entities = Get-CIPPAzDataTableEntity @Table -Filter "PartitionKey eq 'value'" |
| 94 | +Add-CIPPAzDataTableEntity @Table -Entity $Row -Force # Upsert |
| 95 | +``` |
| 96 | + |
| 97 | +### Logging |
| 98 | + |
| 99 | +```powershell |
| 100 | +# General logging (HTTP endpoints, standards, orchestrators, cache, etc.) |
| 101 | +Write-LogMessage -API 'EndpointName' -tenant $TenantFilter -message 'What happened' -sev Info |
| 102 | +
|
| 103 | +# Alert functions only — deduplicates by message + tenant per day |
| 104 | +Write-AlertMessage -message 'Alert description' -tenant $TenantFilter -LogData $ErrorMessage |
| 105 | +``` |
| 106 | + |
| 107 | +- **`Write-AlertMessage`**: Use exclusively in alert functions (`Get-CIPPAlert*`). It is a deduplication wrapper — checks if the same message was already logged today for the tenant, and only writes if new. Internally calls `Write-LogMessage` with `-sev 'Alert'` and `-API 'Alerts'`. |
| 108 | +- **`Write-LogMessage`**: Use everywhere else. Directly writes to the `CippLogs` Azure Table with full audit context. |
| 109 | + |
| 110 | +Severity levels: `Debug`, `Info`, `Warning`, `Error`. Logs go to the `CippLogs` Azure Table. |
| 111 | + |
| 112 | +### Error handling |
| 113 | + |
| 114 | +Use `Get-CippException -Exception $_` (preferred) or `Get-NormalizedError` (legacy) inside `catch` blocks, then `Write-LogMessage` with `-sev Error`. See `powershell-conventions.instructions.md` for full patterns. |
| 115 | + |
| 116 | +## Tenant filtering |
| 117 | + |
| 118 | +Every tenant-scoped operation receives a `$TenantFilter` parameter (domain name or GUID). Access is validated with `Test-CIPPAccess` at the HTTP layer. Always pass `$TenantFilter` (or `$Tenant` in standards) through to Graph/Exchange calls via `-tenantid`. |
| 119 | + |
| 120 | +## Authentication model |
| 121 | + |
| 122 | +CIPP is a **multi-tenant partner management tool**. A single **Secure Application Model (SAM)** app in the partner's tenant accesses all customer tenants via delegated admin (GDAP) or direct tenant relationships. Credentials live in Azure Key Vault; `Get-GraphToken` handles token acquisition, caching, and refresh automatically. Comprehensive documentation (SAM architecture, token flows, scopes, GDAP vs direct tenants, caching, API helpers) is in `.github/instructions/auth-model.instructions.md`, auto-loaded when editing GraphHelper files. |
| 123 | + |
| 124 | +### What developers need to know |
| 125 | + |
| 126 | +- **Never call `Get-GraphToken` directly** — `New-GraphGetRequest`, `New-ExoRequest`, etc. handle token acquisition internally |
| 127 | +- **Always pass `-tenantid`** — without it, the call goes to the partner tenant, not the customer |
| 128 | +- **Different scopes = different tokens**: Graph, Exchange, and Partner Center each have separate tokens |
| 129 | +- **Do not hardcode secrets** — all credentials come from Key Vault via `Get-CIPPAuthentication` |
| 130 | + |
| 131 | +## Function categories |
| 132 | + |
| 133 | +| Category | Location | Naming | Purpose | |
| 134 | +|----------|----------|--------|---------| |
| 135 | +| HTTP endpoints | `Entrypoints/HTTP Functions/` | `Invoke-List*` / `Invoke-Exec*` | Frontend-callable API | |
| 136 | +| Standards | `Standards/` | `Invoke-CIPPStandard*` | Compliance enforcement (remediate/alert/report) | |
| 137 | +| Alerts | `Alerts/` | `Get-CIPPAlert*` | Tenant health monitoring | |
| 138 | +| Orchestrators | `Entrypoints/Orchestrator Functions/` | `Start-*Orchestrator` | Workflow coordination | |
| 139 | +| Activity triggers | `Entrypoints/Activity Triggers/` | `Push-*` | Parallelizable work units | |
| 140 | +| Timer functions | `Entrypoints/Timer Functions/` | `Start-*` | Scheduled background jobs | |
| 141 | +| DB cache | `Public/Set-CIPPDBCache*.ps1` | `Set-CIPPDBCache*` | Tenant data cache refresh | |
| 142 | + |
| 143 | +## CIPP DB (tenant data cache) |
| 144 | + |
| 145 | +CIPPDB is a **tenant-scoped read cache** backed by the `CippReportingDB` Azure Table. Standards, alerts, reports, and the UI read from cache instead of making live API calls. `Set-CIPPDBCache*` functions refresh the cache nightly; `New-CIPPDbRequest` is the primary reader. Comprehensive documentation (CRUD signatures, pipeline streaming, batch writes, collection grouping, scaffolding) is in `.github/instructions/cippdb.instructions.md`, auto-loaded when editing DB-related files. |
| 146 | + |
| 147 | +## Coding conventions |
| 148 | + |
| 149 | +Detailed PowerShell coding conventions are in `.github/instructions/powershell-conventions.instructions.md`, auto-loaded when editing `.ps1` files. Covers naming, collection building, pipeline usage, null handling, error handling, JSON serialization, and PS 7.4 idioms. |
| 150 | + |
| 151 | +## Configuration |
| 152 | + |
| 153 | +- **`host.json`** — Runtime config (timeouts, concurrency limits, extension bundles) |
| 154 | +- **`CIPPTimers.json`** — Scheduled task definitions with priorities and cron expressions |
| 155 | +- **`Config/`** — JSON templates for CA policies, Intune profiles, transport rules, BPA |
| 156 | +- **Environment variables** — `AzureWebJobsStorage`, `APPLICATIONINSIGHTS_CONNECTION_STRING`, `CIPP_PROCESSOR`, `DebugMode` |
| 157 | + |
| 158 | +## Things to avoid |
| 159 | + |
| 160 | +- Do not install modules from the Gallery — bundle everything locally |
| 161 | +- Do not modify module manifests to register new functions — auto-loading handles it |
| 162 | +- Do not create new Azure Function trigger folders — use the existing five triggers |
| 163 | +- Do not call `Write-Output` in HTTP functions — return an `[HttpResponseContext]` (the outer trigger handles `Push-OutputBinding`) |
| 164 | +- Do not hardcode tenant IDs or secrets — use environment variables and `Get-GraphToken` |
0 commit comments