// src/generators/intune-install.ts // Generates a PowerShell install script suitable for deployment via Microsoft Intune. // // Key design constraints: // - UTF-8 no-BOM encoding: [System.IO.File]::WriteAllText with UTF8Encoding($false) // (Set-Content -Encoding UTF8 in PS 5.1 writes UTF-8 WITH BOM — do not use) // - Config content embedded as PS here-string (@'...'@) — no PS variable interpolation // inside the block, so credentials are written as literals (SECU requirement) // - $ErrorActionPreference = 'Stop' is appropriate here (install scripts run in full PS host) import type { WizardState } from '../store/types'; import { CONFIG_DIR, buildRcloneInstallBlock } from './ps-helpers'; export function buildIntuneInstall(state: WizardState): string { const configDir = CONFIG_DIR[state.deployment.configPath]; const confContent = buildRcloneConfContent(state); const installBlock = state.deployment.includeInstall ? buildRcloneInstallBlock(configDir) : ''; return [ `$ErrorActionPreference = 'Stop'`, ``, `$configDir = '${configDir}'`, `$configPath = "$configDir\\rclone.conf"`, ``, `# Idempotent: create directory if it doesn't exist`, `if (-not (Test-Path $configDir)) {`, ` New-Item -ItemType Directory -Path $configDir -Force | Out-Null`, `}`, ``, `# Write config using UTF-8 no-BOM encoding`, `# Note: Set-Content -Encoding UTF8 in PS 5.1 writes UTF-8 WITH BOM — do not use`, `$confContent = @'`, confContent.trimEnd(), `'@`, `[System.IO.File]::WriteAllText($configPath, $confContent, [System.Text.UTF8Encoding]::new($false))`, installBlock, ``, `exit 0`, ].join('\n') + '\n'; } // Build the rclone.conf content to embed in the here-string. // Credentials are written as literal values — no PS variable interpolation. // Inlines the INI format rather than importing buildRcloneConf to avoid // coupling generators together and risking circular imports. function buildRcloneConfContent(state: WizardState): string { const RCLONE_TYPE_MAP: Record = { azureblob: 'azureblob', s3: 's3', 's3-compatible': 's3', }; const { name, backendType, params } = state.remote; const rcloneType = backendType ? RCLONE_TYPE_MAP[backendType] ?? backendType : 'unknown'; const lines: string[] = [`[${name}]`, `type = ${rcloneType}`]; for (const [key, value] of Object.entries(params)) { if (value !== '') lines.push(`${key} = ${value}`); } return lines.join('\n') + '\n'; }