Files
Ready2Blob/src/generators/intune-install.ts
T
kawa 495be0cd11 feat(02-03): implement buildIntuneInstall and buildIntuneDetection
- buildIntuneInstall: UTF-8 no-BOM write via WriteAllText, PS here-string for config, conditional rclone download block
- buildIntuneDetection: no $ErrorActionPreference, Write-Output + exit 0/1 pattern
- Both generators use CONFIG_DIR from ps-helpers for consistent path resolution
- All 15 intune-install and intune-detection tests pass GREEN
2026-03-26 14:06:45 +01:00

62 lines
2.5 KiB
TypeScript

// 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<string, string> = {
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';
}