#!/usr/bin/env node // Print a signed, temporary download URL for the Windows 11 ISO by driving // Microsoft's download page with headless Chromium. // // Usage: node get-windows-iso-url.js [--arch x64|arm64] [--lang NAME] // Environment: // PLAYWRIGHT_CORE playwright-core directory (default: node_modules here) // CHROME_PATH Chromium executable (default: the one Playwright installed) // The URL expires after about 24 hours and may be bound to the IP that // requested it. Only the URL goes to stdout; logs go to stderr. const fs = require('fs'); const path = require('path'); const args = process.argv.slice(2); if (args.includes('--help') || args.includes('-h')) { console.log(`Usage: node get-windows-iso-url.js [options] Print a signed, temporary download URL for the Windows 11 ISO (only the URL goes to stdout; logs go to stderr). Options: --arch Architecture, default: the host's (x64 on amd64 hosts) --lang Language, default "English (United States)", for example "Chinese Simplified" -h, --help Show this help Environment: PLAYWRIGHT_CORE playwright-core directory (default: node_modules here) CHROME_PATH Chromium executable (default: the one Playwright installed) Examples: node get-windows-iso-url.js --arch x64 --lang "Chinese Simplified" wget -c "$(node get-windows-iso-url.js 2>/dev/null)" The URL expires after about 24 hours and may be bound to the IP that requested it.`); process.exit(0); } const opt = (name, def) => { const i = args.indexOf(name); return i < 0 ? def : (args[i + 1] && !args[i + 1].startsWith('--') ? args[i + 1] : true); }; const hostArch = { x64: 'x64', arm64: 'arm64' }[process.arch]; const arch = opt('--arch', hostArch); const lang = opt('--lang', 'English (United States)'); const pages = { arm64: 'windows11arm64', x64: 'windows11' }; if (!pages[arch]) { console.error(arch === undefined ? `unsupported host architecture ${process.arch}: pass --arch x64|arm64` : '--arch must be x64 or arm64'); process.exit(2); } const log = (...a) => console.error('[iso]', ...a); function findPlaywright() { if (process.env.PLAYWRIGHT_CORE) return process.env.PLAYWRIGHT_CORE; try { return path.dirname(require.resolve('playwright-core/package.json')); } catch {} console.error('playwright-core not found: run npm ci or set PLAYWRIGHT_CORE'); process.exit(2); } // CHROME_PATH wins; otherwise Playwright picks the Chromium it installed (in // its own per-OS cache, PLAYWRIGHT_BROWSERS_PATH if set). function launchOptions(chromium) { if (process.env.CHROME_PATH) return { executablePath: process.env.CHROME_PATH, headless: true }; if (!fs.existsSync(chromium.executablePath())) { console.error('Chromium not found: set CHROME_PATH or run npx playwright-core install chromium'); process.exit(2); } return { headless: true }; } (async () => { const { chromium } = require(findPlaywright()); const browser = await chromium.launch(launchOptions(chromium)); try { const ctx = await browser.newContext({ locale: 'en-US', userAgent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0 Safari/537.36', }); const page = await ctx.newPage(); let api = null; page.on('response', async r => { if (/GetProductDownloadLinks/i.test(r.url())) api = await r.json().catch(() => null); }); log('opening', pages[arch]); await page.goto(`https://www.microsoft.com/en-us/software-download/${pages[arch]}`, { waitUntil: 'networkidle', timeout: 60000 }); await page.click('button:has-text("Accept")', { timeout: 3000 }).catch(() => {}); // Pick the first real edition (currently the only multi-edition ISO). const edition = await page.$$eval('#product-edition option', o => o.map(x => x.value).find(v => v && v !== 'null')); if (!edition) throw new Error('no edition option found; the page layout may have changed'); await page.selectOption('#product-edition', edition); await page.click('#submit-product-edition'); await page.waitForSelector('#product-languages option:not([value="null"])', { state: 'attached', timeout: 60000 }); const langs = await page.$$eval('#product-languages option', o => o.map(x => ({ value: x.value, text: x.text }))); const hit = langs.find(l => l.text === lang); if (!hit) throw new Error(`no language "${lang}"; available: ${langs.map(l => l.text).filter(t => t !== 'Choose one').join(', ')}`); await page.selectOption('#product-languages', hit.value); await page.click('#submit-sku'); await page.waitForSelector('a[href*="prss.microsoft.com"]', { timeout: 60000 }); const url = await page.$eval('a[href*="prss.microsoft.com"]', a => a.href); const opts = api && api.ProductDownloadOptions; log(opts ? opts[0].Name : '(API response not captured)', '| expires:', api && api.DownloadExpirationDatetime); console.log(url); } finally { await browser.close(); } })().catch(e => { console.error('[iso] failed:', e.message); process.exit(1); });