"""Script generator module — renders PowerShell scripts from Jinja2 templates. Provides render_install(), render_uninstall(), and render_detect() which produce complete PowerShell scripts for Intune deployment: - render_install: WOW64 guard, UAC self-elevation, pnputil two-step, idempotent setup - render_uninstall: Ordered removal of printer, driver, and port - render_detect: Intune detection contract (Write-Output + exit 0/1) """ from pathlib import Path from jinja2 import Environment, FileSystemLoader _SCRIPTS_DIR = Path(__file__).parent.parent / "templates" / "scripts" _env = Environment( loader=FileSystemLoader(str(_SCRIPTS_DIR)), trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True, ) _duplex_map = { "OneSided": "OneSided", "LongEdge": "TwoSidedLongEdge", "ShortEdge": "TwoSidedShortEdge", } def render_install( printer_name: str, ip_address: str, port_name: str, driver_name: str, inf_filename: str, duplex_mode: str, color_mode: bool, paper_size: str, collate: bool, ) -> str: """Render install.ps1.j2 with the given printer configuration. Args: printer_name: Display name of the printer. ip_address: IP address for the printer TCP/IP port. port_name: Port name (e.g. "IP_192.168.1.10"). driver_name: Exact driver name as registered in Windows. inf_filename: INF filename inside the drivers/ subfolder. duplex_mode: One of "OneSided", "LongEdge", "ShortEdge" (model values). color_mode: True for color printing, False for mono. paper_size: Paper size string (e.g. "A4", "Letter"). collate: True to enable collation. Returns: Rendered PowerShell script as a string. """ tpl = _env.get_template("install.ps1.j2") return tpl.render( printer_name=printer_name, ip_address=ip_address, port_name=port_name, driver_name=driver_name, inf_filename=inf_filename, duplex_mode=_duplex_map.get(duplex_mode, duplex_mode), color=str(color_mode).lower(), paper_size=paper_size, collate=str(collate).lower(), ) def render_uninstall(printer_name: str, driver_name: str, port_name: str) -> str: """Render uninstall.ps1.j2 — removes printer, driver, and port in safe order. Removal order: Printer first (so driver is no longer referenced), then Driver, then Port. All operations use -ErrorAction SilentlyContinue for idempotency. Args: printer_name: Display name of the printer to remove. driver_name: Exact driver name as registered in Windows. port_name: Port name (e.g. "IP_192.168.1.10"). Returns: Rendered PowerShell script as a string. """ tpl = _env.get_template("uninstall.ps1.j2") return tpl.render( printer_name=printer_name, driver_name=driver_name, port_name=port_name, ) def render_detect(printer_name: str) -> str: """Render detect.ps1.j2 — Intune detection script. Follows the Intune detection contract: Write-Output + exit 0 when printer is found, exit 1 when absent. Intune considers exit 0 as "app installed". Args: printer_name: Display name of the printer to detect. Returns: Rendered PowerShell script as a string. """ tpl = _env.get_template("detect.ps1.j2") return tpl.render(printer_name=printer_name)