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.0.tar.gz (43.9 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.0-py3-none-any.whl (35.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: pymdm-0.4.0.tar.gz
  • Upload date:
  • Size: 43.9 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.0.tar.gz
Algorithm Hash digest
SHA256 8dc18e1f0d765667af91429955b97abfed3c44ea0fce475990e011d08b0a342c
MD5 0bdddcdae637e1800e2e617f03ebea7c
BLAKE2b-256 53552e71d872881ad15c4e86e15625f8b1106f482a3e9f89605fbde35fcbd319

See more details on using hashes here.

Provenance

The following attestation bundles were made for pymdm-0.4.0.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.0-py3-none-any.whl.

File metadata

  • Download URL: pymdm-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 35.1 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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 bda1b208950e2e6ecb8045f3595ac254e667faa5c24dbe105a146641e527ac38
MD5 97c40f742ea0bfe792354eaf75cb4b9c
BLAKE2b-256 f1e9213837aaf1fc2c3cc5068598b238843e5be67f8a40cd679c7af8cc96af99

See more details on using hashes here.

Provenance

The following attestation bundles were made for pymdm-0.4.0-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