graph
napt.graph.client
Microsoft Graph HTTP transport shared by every Graph caller in NAPT.
Provides graph_request, the single function through which Intune app management, assignment, and app registration calls reach Graph, along with the header builders callers pass to it. Endpoint-specific wrappers live in napt.graph.intune and napt.auth.registration.
Graph calls retry transient failures -- HTTP 429 (honoring Retry-After), transient server errors, and connection drops -- with bounded exponential backoff before raising. Resource-creating POSTs retry only unambiguous throttling responses, so a lost reply to a processed create is never resubmitted as a duplicate.
auth_headers
Returns the Authorization header for a bodiless Graph request.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
access_token
|
str
|
Bearer token for Graph API. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, str]
|
Headers carrying the bearer token. |
Source code in napt/graph/client.py
json_headers
Returns the headers for a Graph request with a JSON body.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
access_token
|
str
|
Bearer token for Graph API. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, str]
|
Headers carrying the bearer token and a JSON content type. |
Source code in napt/graph/client.py
graph_request
graph_request(
method: str,
url: str,
context: str,
headers: dict[str, str],
json: dict | None = None,
ok_statuses: tuple[int, ...] = (),
idempotent: bool = True,
deadline: float | None = None,
) -> dict
Issues a Graph API request, retrying transient failures.
HTTP 429 (honoring Retry-After) and transient server errors
retry with exponential backoff, as do connection-level failures.
Non-idempotent calls (resource-creating POSTs) retry only statuses
that guarantee the request was shed before processing, and never
connection failures — a lost reply to a processed create must not
be resubmitted. Every other response is checked immediately, so
permission and validation errors surface without retrying. The last
attempt's failure is raised with full response detail. Each request
carries a fresh client-request-id header for Microsoft support
correlation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method
|
str
|
HTTP method name. |
required |
url
|
str
|
Full request URL. |
required |
context
|
str
|
Short description of the operation for error messages. |
required |
headers
|
dict[str, str]
|
Request headers, including authorization. |
required |
json
|
dict | None
|
Optional JSON body. |
None
|
ok_statuses
|
tuple[int, ...]
|
Statuses to treat as success with an empty body (e.g. 404 for an idempotent delete). |
()
|
idempotent
|
bool
|
Whether resubmitting this request is always safe. False restricts retries to unambiguous throttling responses. |
True
|
deadline
|
float | None
|
Optional |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
Parsed JSON body as a dict, or empty dict for empty responses |
dict
|
and |
Raises:
| Type | Description |
|---|---|
AuthError
|
On 401 or 403. |
ConfigError
|
On 400. |
NetworkError
|
On any other non-2xx status once retries are exhausted, or on a connection failure. |
Source code in napt/graph/client.py
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 | |
napt.graph.intune
Intune app management calls: Win32 app upload, queries, and assignments.
Implements the full upload flow for a Win32 LOB app:
1. Create Win32 app record in Intune (POST mobileApps)
2. Create a content version (POST contentVersions)
3. Create a file entry and wait for SAS URI (POST files + polling)
4. Upload encrypted payload to Azure Blob Storage (PUT blocks + block list)
5. Commit the uploaded file with encryption metadata (POST commit + polling)
6. Set the committed content version on the app (PATCH mobileApps)
Also provides app queries used for reconciliation (list_mobile_apps, get_mobile_app, update_win32_app) and group-based assignment plumbing (resolve_group_id, resolve_assignment_target, get_app_assignments, build_assignment, assign_app) used by deployment promotion.
All functions take an access_token as the first argument. Obtain one via get_access_token. Graph calls go through graph_request and inherit its retry behavior; Azure Blob PUTs carry their own retry tuned for SAS-propagation 403s.
resolve_group_id
Resolves an Entra ID group name or object ID to an object ID.
Values that already look like object IDs (GUIDs) pass through without a Graph call. Names are looked up by exact displayName match, which requires the Group.Read.All application permission.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
access_token
|
str
|
Bearer token for Graph API. |
required |
group
|
str
|
Group displayName or object ID (GUID). |
required |
Returns:
| Type | Description |
|---|---|
str
|
The group's object ID. |
Raises:
| Type | Description |
|---|---|
AuthError
|
On 401 or 403 (check Group.Read.All permission). |
ConfigError
|
If no group or more than one group matches the name. |
NetworkError
|
On 5xx or connection error. |
Source code in napt/graph/intune.py
resolve_assignment_target
resolve_assignment_target(
access_token: str, group: str, group_id_cache: dict[str, str] | None = None
) -> dict
Resolves a deployment group entry to an assignment target dict.
The reserved names "All Users" and "All Devices" map to Intune's built-in virtual targets; anything else resolves to an Entra ID group target via resolve_group_id.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
access_token
|
str
|
Bearer token for Graph API. |
required |
group
|
str
|
Group displayName, object ID, or reserved virtual name. |
required |
group_id_cache
|
dict[str, str] | None
|
Optional cache of name to object ID, shared across calls to avoid repeated lookups. |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
An assignment target dict for use with build_assignment. |
Raises:
| Type | Description |
|---|---|
AuthError
|
On 401 or 403 (check Group.Read.All permission). |
ConfigError
|
If no group or more than one group matches a name. |
NetworkError
|
On 5xx or connection error. |
Source code in napt/graph/intune.py
get_app_assignments
Gets the current assignments of a mobile app.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
access_token
|
str
|
Bearer token for Graph API. |
required |
app_id
|
str
|
Graph API object ID of the app. |
required |
Returns:
| Type | Description |
|---|---|
list[dict]
|
A list of mobileAppAssignment dicts (empty when unassigned). |
Raises:
| Type | Description |
|---|---|
AuthError
|
On 401 or 403. |
NetworkError
|
On 5xx or connection error. |
Source code in napt/graph/intune.py
build_assignment
Builds a mobileAppAssignment payload for a resolved target.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
dict
|
An assignment target dict (group or virtual target). |
required |
intent
|
str
|
Assignment intent, "available" or "required". |
required |
Returns:
| Type | Description |
|---|---|
dict
|
A mobileAppAssignment dict for use with assign_app. |
Source code in napt/graph/intune.py
assign_app
Sets a mobile app's assignments.
The assign action replaces the app's entire assignment set. Callers that intend to preserve existing assignments must read them first with get_app_assignments and include them in the new list.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
access_token
|
str
|
Bearer token for Graph API. |
required |
app_id
|
str
|
Graph API object ID of the app. |
required |
assignments
|
list[dict]
|
Complete list of mobileAppAssignment dicts to apply. |
required |
Raises:
| Type | Description |
|---|---|
AuthError
|
On 401 or 403. |
ConfigError
|
On 400 (invalid assignment payload). |
NetworkError
|
On 5xx or connection error. |
Source code in napt/graph/intune.py
list_mobile_apps
Lists all mobile apps in the tenant with id, displayName, and notes.
Follows @odata.nextLink pagination until the collection is exhausted.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
access_token
|
str
|
Bearer token for Graph API. |
required |
Returns:
| Type | Description |
|---|---|
list[dict]
|
A list of app dicts, each with at least "id", "displayName", and "notes" keys. |
Raises:
| Type | Description |
|---|---|
AuthError
|
On 401 or 403. |
NetworkError
|
On 5xx or connection error. |
Source code in napt/graph/intune.py
delete_mobile_app
Deletes a mobile app from Intune.
A 404 is tolerated — the app being already gone is the desired end state, so retried deletions stay idempotent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
access_token
|
str
|
Bearer token for Graph API. |
required |
app_id
|
str
|
Graph API object ID of the app to delete. |
required |
Raises:
| Type | Description |
|---|---|
AuthError
|
On 401 or 403. |
NetworkError
|
On 5xx or connection error. |
Source code in napt/graph/intune.py
get_mobile_app
Gets one mobile app's full object by Graph API ID.
Used to read subtype fields that $select on the collection cannot reliably return, such as win32LobApp.committedContentVersion.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
access_token
|
str
|
Bearer token for Graph API. |
required |
app_id
|
str
|
Graph API object ID of the app. |
required |
Returns:
| Type | Description |
|---|---|
dict
|
The full app object dict. |
Raises:
| Type | Description |
|---|---|
AuthError
|
On 401 or 403. |
NetworkError
|
On 5xx or connection error. |
Source code in napt/graph/intune.py
create_win32_app
Creates a new Win32 LOB app record in Intune.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
access_token
|
str
|
Bearer token for Graph API. |
required |
app_metadata
|
dict
|
Win32LobApp JSON payload (display name, install commands, detection rules, etc.). |
required |
Returns:
| Type | Description |
|---|---|
str
|
The Graph API object ID of the newly created app. |
Raises:
| Type | Description |
|---|---|
AuthError
|
On 401 or 403. |
ConfigError
|
On 400 (invalid metadata). |
NetworkError
|
On 5xx or connection error. |
Source code in napt/graph/intune.py
update_win32_app
Updates an existing Win32 LOB app record's metadata in Intune.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
access_token
|
str
|
Bearer token for Graph API. |
required |
app_id
|
str
|
Graph API object ID of the app to update. |
required |
app_metadata
|
dict
|
Win32LobApp JSON payload to apply. |
required |
Raises:
| Type | Description |
|---|---|
AuthError
|
On 401 or 403. |
ConfigError
|
On 400 (invalid metadata). |
NetworkError
|
On 5xx or connection error. |
Source code in napt/graph/intune.py
create_content_version
Creates a new content version for a Win32 app.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
access_token
|
str
|
Bearer token for Graph API. |
required |
app_id
|
str
|
Graph API object ID of the Win32 app. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The content version ID string. |
Raises:
| Type | Description |
|---|---|
AuthError
|
On 401 or 403. |
NetworkError
|
On 5xx or connection error. |
Source code in napt/graph/intune.py
create_content_version_file
create_content_version_file(
access_token: str, app_id: str, cv_id: str, metadata: IntunewinMetadata
) -> tuple[str, str]
Creates a file entry for a content version and waits for the SAS URI.
Posts the file size information to Graph API, then polls until Azure Storage has provisioned a SAS URI for the upload.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
access_token
|
str
|
Bearer token for Graph API. |
required |
app_id
|
str
|
Graph API object ID of the Win32 app. |
required |
cv_id
|
str
|
Content version ID from create_content_version. |
required |
metadata
|
IntunewinMetadata
|
Parsed .intunewin metadata (provides file sizes). |
required |
Returns:
| Type | Description |
|---|---|
tuple[str, str]
|
A tuple of (file_id, sas_uri) where sas_uri is the Azure Blob Storage SAS URI to upload the encrypted payload to. |
Raises:
| Type | Description |
|---|---|
AuthError
|
On 401 or 403. |
NetworkError
|
On 5xx, connection error, or upload state error. |
Source code in napt/graph/intune.py
upload_to_azure_blob
Uploads the encrypted payload to Azure Blob Storage using block blobs.
Splits the file into CHUNK_SIZE chunks, uploads each as a block with a base64-encoded block ID, then commits the block list. Prints an inline progress percentage as each chunk completes. Transient per-request failures (including 403 from a not-yet-propagated SAS URI) are retried with backoff.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sas_uri
|
str
|
Azure Blob Storage SAS URI from create_content_version_file. |
required |
encrypted_payload_path
|
Path
|
Path to the extracted encrypted payload file (IntunePackage.intunewin from inside the .intunewin ZIP). |
required |
Raises:
| Type | Description |
|---|---|
NetworkError
|
If any block upload or the block list commit fails after retries. |
Source code in napt/graph/intune.py
563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 | |
commit_content_version_file
commit_content_version_file(
access_token: str,
app_id: str,
cv_id: str,
file_id: str,
metadata: IntunewinMetadata,
) -> None
Commits the uploaded file with encryption metadata, then waits for confirmation.
Sends the encryption key, MAC, IV, and digest to Graph API, then polls until Intune confirms the file is committed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
access_token
|
str
|
Bearer token for Graph API. |
required |
app_id
|
str
|
Graph API object ID of the Win32 app. |
required |
cv_id
|
str
|
Content version ID. |
required |
file_id
|
str
|
File entry ID from create_content_version_file. |
required |
metadata
|
IntunewinMetadata
|
Parsed .intunewin metadata (provides all encryption fields). |
required |
Raises:
| Type | Description |
|---|---|
AuthError
|
On 401 or 403. |
NetworkError
|
On 5xx, connection error, or if commit times out. |
Note
Graph returns 200 (not 201) for the commit POST.
Source code in napt/graph/intune.py
commit_content_version
Sets the committed content version on the Win32 app.
This is the final step — after calling this, the app is fully published in Intune and available for assignment.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
access_token
|
str
|
Bearer token for Graph API. |
required |
app_id
|
str
|
Graph API object ID of the Win32 app. |
required |
cv_id
|
str
|
Content version ID to mark as committed. |
required |
Raises:
| Type | Description |
|---|---|
AuthError
|
On 401 or 403. |
NetworkError
|
On 5xx or connection error. |