Skip to main content

Cross-platform utility package for MDM deployment scripts (Jamf Pro, Intune).

Project description

pymdm

A Python utility package for macOS MDM deployment scripts, built for MacAdmins Python (#!/usr/local/bin/managed_python3) and Jamf Pro workflows. Windows/Intune support is also available for teams managing mixed-platform fleets.

Features

  • ParamParser: Safe parsing of Jamf Pro script parameters 4-11 (macOS)
  • Dialog: swiftDialog integration for user-facing dialogs and notifications (macOS)
  • CommandRunner: Secure subprocess execution with credential sanitization and platform-aware run-as-user
  • SystemInfo: System information helpers — serial number, console user, hostname
  • MdmLogger: Structured logging with file output, rotation, and multiple log levels
  • WebhookSender: Send logs and metadata to webhooks
  • IntuneParamProvider: Env var and argv parameter parsing for Intune scripts (Windows)

Platform Support

Feature macOS (Jamf) Windows (Intune)
ParamParser (Jamf) Yes
Dialog (swiftDialog) Yes Graceful no-op
CommandRunner Yes Yes
SystemInfo Yes Yes
MdmLogger Yes Yes
WebhookSender Yes Yes
IntuneParamProvider Yes

Installation

From Source

uv pip install -e .

Development

make install     # Install with dev dependencies
make test        # Run tests
make format      # Format code with ruff

Quick Start

Logging

from pymdm import MdmLogger

logger = MdmLogger(
    debug=True,
    output_path="/var/log/my_script.log"
)

logger.info("Script started")
logger.debug("Detailed information")
logger.warn("Warning message")
logger.error("Error occurred", exit_code=1)

Jamf Parameters (macOS)

from pymdm import ParamParser

# Get string parameter
webhook_url = ParamParser.get(4)  # $4 in Jamf policy

# Get boolean parameter
debug_mode = ParamParser.get_bool(5)  # "true", "1", "yes" -> True

# Get integer parameter
timeout = ParamParser.get_int(6, default=30)

Intune Parameters (Windows)

from pymdm.mdm import IntuneParamProvider

provider = IntuneParamProvider()

# Get from sys.argv
value = provider.get(1)

# Get from environment variable
webhook_url = provider.get("WEBHOOK_URL")

# Boolean from env var
debug = provider.get_bool("DEBUG_MODE")

Auto-Detect MDM Provider

from pymdm.mdm import get_provider

# Automatically selects Jamf on macOS, Intune on Windows
# Override with PYMDM_MDM_PROVIDER env var
provider = get_provider()

value = provider.get(4)  # Jamf: sys.argv[4], Intune: sys.argv[4]
debug = provider.get_bool("DEBUG")  # Intune: env var lookup

Command Execution

from pymdm import CommandRunner

runner = CommandRunner(logger=logger)

# Safe execution (list form)
output = runner.run(["/usr/bin/id", "-u", username])

# Shell execution (for pipes, etc.)
output = runner.run("ps aux | grep python", timeout=10)

# Run as logged-in user (platform-aware)
# macOS: uses launchctl asuser
# Windows: uses PowerShell Start-Process
runner = CommandRunner(logger=logger, username="jsmith", uid=501)
output = runner.run_as_user(["/usr/bin/open", "-a", "Safari"])

System Information

from pymdm import SystemInfo

# Get serial number
# macOS: system_profiler | Windows: PowerShell/wmic
serial = SystemInfo.get_serial_number()

# Get console user info
user_info = SystemInfo.get_console_user()
if user_info:
    username, uid, home_path = user_info

# Get hostname
hostname = SystemInfo.get_hostname()

# Get full name
full_name = SystemInfo.get_user_full_name("jsmith")

Webhook Integration

from pymdm import WebhookSender, MdmLogger

logger = MdmLogger(output_path="/var/log/script.log")
webhook = WebhookSender(
    url="https://hooks.tray.io/...",
    logger=logger
)

# Send log with metadata
webhook.send(
    hostname=SystemInfo.get_hostname(),
    serial=SystemInfo.get_serial_number(),
    script_name="my_deployment_script",
    status="success"
)

Complete Example (macOS / Jamf Pro)

#!/usr/local/bin/managed_python3
"""Example Jamf Pro policy script."""

from pymdm import (
    MdmLogger,
    ParamParser,
    CommandRunner,
    SystemInfo,
    WebhookSender,
)

# Setup
logger = MdmLogger(
    debug=ParamParser.get_bool(4),
    output_path="/var/log/my_script.log"
)
runner = CommandRunner(logger=logger)

logger.log_startup("my_script", version="1.0.0")

try:
    # Get system info
    serial = SystemInfo.get_serial_number()
    hostname = SystemInfo.get_hostname()

    logger.info(f"Running on {hostname} ({serial})")

    # Execute command
    output = runner.run(["/usr/bin/sw_vers", "-productVersion"])
    logger.info(f"macOS version: {output}")

    # Send results
    webhook = WebhookSender(
        url=ParamParser.get(5),
        logger=logger
    )
    webhook.send(
        hostname=hostname,
        serial=serial,
        status="success"
    )

except Exception as e:
    logger.log_exception("Script failed", e, exit_code=1)

Complete Example (Windows / Intune)

"""Example Intune deployment script."""

from pymdm import MdmLogger, CommandRunner, SystemInfo, WebhookSender
from pymdm.mdm import IntuneParamProvider

# Setup
params = IntuneParamProvider()
logger = MdmLogger(
    debug=params.get_bool("DEBUG"),
    output_path="C:\\ProgramData\\Scripts\\my_script.log"
)
runner = CommandRunner(logger=logger)

logger.log_startup("my_script", version="1.0.0")

try:
    serial = SystemInfo.get_serial_number()
    hostname = SystemInfo.get_hostname()

    logger.info(f"Running on {hostname} ({serial})")

    # Windows-specific command
    output = runner.run(["powershell", "-Command", "Get-ComputerInfo | Select-Object OsVersion"])
    logger.info(f"System info: {output}")

    webhook_url = params.get("WEBHOOK_URL")
    if webhook_url:
        webhook = WebhookSender(url=webhook_url, logger=logger)
        webhook.send(hostname=hostname, serial=serial, status="success")

except Exception as e:
    logger.log_exception("Script failed", e, exit_code=1)

Platform Configuration

Environment Variables

Variable Purpose Values
PYMDM_PLATFORM Override platform auto-detection darwin, win32
PYMDM_MDM_PROVIDER Override MDM provider auto-detection jamf, intune

Task Mapping: Jamf vs Intune

Jamf Pro Intune Equivalent pymdm API
ParamParser.get(4) IntuneParamProvider().get("PARAM_NAME") get_provider().get(...)
ParamParser.get_bool(5) IntuneParamProvider().get_bool("FLAG") get_provider().get_bool(...)
Script params via sys.argv[4-11] Env vars or sys.argv Provider-specific
jamf recon Microsoft Graph API Not in pymdm (use provider SDK)
swiftDialog Windows toast/WPF Dialog (macOS only)

Migration Notes

From pymdm < 0.4.0

All existing imports and APIs are fully backward compatible. No changes required for macOS/Jamf scripts:

# These all work exactly as before
from pymdm import SystemInfo, CommandRunner, ParamParser, MdmLogger

For new Windows/Intune scripts, use the new provider APIs:

from pymdm.mdm import IntuneParamProvider, get_provider
from pymdm.platforms import get_platform

Architecture

pymdm/
├── platforms/          # OS-specific implementations
│   ├── darwin.py       # macOS: system_profiler, launchctl
│   └── win32.py        # Windows: PowerShell, wmic, runas
├── mdm/                # MDM provider implementations
│   ├── jamf.py         # Jamf Pro: sys.argv[4-11] parsing
│   └── intune.py       # Intune: env vars, flexible argv
├── command_runner.py   # Cross-platform subprocess wrapper
├── dialog.py           # swiftDialog integration (macOS)
├── logger.py           # Structured logging
├── param_parser.py     # Backward-compat Jamf parser facade
├── system_info.py      # Cross-platform system info facade
└── webhook_sender.py   # HTTP webhook sender

Requirements

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pymdm-0.4.2.tar.gz (44.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pymdm-0.4.2-py3-none-any.whl (35.2 kB view details)

Uploaded Python 3

File details

Details for the file pymdm-0.4.2.tar.gz.

File metadata

  • Download URL: pymdm-0.4.2.tar.gz
  • Upload date:
  • Size: 44.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pymdm-0.4.2.tar.gz
Algorithm Hash digest
SHA256 b46e5f3577d8cd7e0ad729bd62255245c4e96c31bdd65dfa3592e4ad4821ae43
MD5 342a37a1b6d3a43d349863845c2717d9
BLAKE2b-256 13123ef9e9bda87f7acc49162d98de7c6334589256037b01c644db37ac41e3e7

See more details on using hashes here.

Provenance

The following attestation bundles were made for pymdm-0.4.2.tar.gz:

Publisher: build-release.yml on liquidz00/pymdm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pymdm-0.4.2-py3-none-any.whl.

File metadata

  • Download URL: pymdm-0.4.2-py3-none-any.whl
  • Upload date:
  • Size: 35.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pymdm-0.4.2-py3-none-any.whl
Algorithm Hash digest
SHA256 338688ea71fe10516df3d7b57898aac2518c8b53f7bbd69531996a1e4c04f91f
MD5 768032d78547ad7d87ec3a68c12fa3eb
BLAKE2b-256 e9ecd8f1d117390c61d49a94ed9c59afc32c2227e6f10223ef3a65dcc67f1412

See more details on using hashes here.

Provenance

The following attestation bundles were made for pymdm-0.4.2-py3-none-any.whl:

Publisher: build-release.yml on liquidz00/pymdm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page