#!/usr/bin/env node import { execSync, spawnSync } from "child_process"; import { writeFileSync } from "fs"; import { parseArgs } from "util"; const args = parseArgs({ options: { "app-name": { type: "string", short: "a" }, help: { type: "boolean", short: "h" }, "generate-client-secret": { type: "boolean", short: "s" }, }, }); const config = {}; if (args.values["app-name"]) { config.appName = args.values["app-name"]; } else { config.appName = "Azure Node Playground"; } // Get the tenant ID try { config.tenantId = execSync(`az account show --query "tenantId" -o tsv`) .toString() .trim(); } catch (error) { console.error("Failed to get Azure tenant ID."); process.exit(1); } if (config.tenantId) { console.log(`Using Tenant ID: ${config.tenantId}`); } else { console.error("Failed to get Azure tenant ID."); process.exit(1); } try { const appIdList = JSON.parse( execSync( `az ad app list --filter "displayName eq '${config.appName}'" -o json`, ) .toString() .trim(), ); if (appIdList.length !== 1) { throw new Error("App not found or multiple apps with the same name."); } config.appId = appIdList[0].appId; } catch (error) { console.error("Failed to query Azure AD applications."); process.exit(1); } if (config.appId) { console.log(`App already exists with ID: ${config.appId}`); } else { // Create the Azure AD application, capturing the JSON output, and extracting the .appId try { config.appId = execSync( `az ad app create --display-name "${config.appName}" --query "appId" -o tsv`, ) .toString() .trim(); } catch (error) { console.error("Failed to create Azure AD application."); process.exit(1); } console.log(`Created App with ID: ${config.appId}`); } // Chech if service principal exists let spObjId; try { const spIdList = JSON.parse( execSync(`az ad sp list --filter "appId eq '${config.appId}'" -o json`) .toString() .trim(), ); if (spIdList.length === 1) { spObjId = spIdList[0].id; } else { spObjId = null; } } catch (error) { console.error("Failed to query service principals."); process.exit(1); } if (spObjId) { console.log("Using existing service principal."); } else { // Now create the service principal for the app try { spObjId = execSync( `az ad sp create --id ${config.appId} --query "id" -o tsv`, ); console.log("Service principal created."); } catch (error) { console.log("Failed to create service principal."); } } if (args.values["generate-client-secret"]) { // Generate a new client secret for the application try { const result = spawnSync( "az", [ "ad", "app", "credential", "reset", "--id", config.appId, "--query", "password", "-o", "tsv", ], { encoding: "utf-8" }, ); config.clientSecret = result.stdout.toString().trim(); console.log("Client secret generated."); } catch (error) { console.error("Failed to generate client secret."); console.error(error); process.exit(1); } } // Write the APP_ID to the .env file const envContent = `AZ_APP_NAME="${config.appName}" ARM_TENANT_ID=${config.tenantId} ARM_CLIENT_ID=${config.appId} ARM_CLIENT_SECRET=${config.clientSecret || ""} `; writeFileSync(".env", envContent); console.log(".env file created with application configuration."); // Save the config to the 'config.js' file. writeFileSync( "config.js", `export const config = ${JSON.stringify(config, null, 4)};\n`, ); console.log("config.js file created."); // Check if we can change file mode permissions (Unix-like systems) // for sensitive files like .env and config.js. try { execSync("chmod 600 .env config.js"); console.log("File permissions for .env and config.js set to 600."); } catch (error) { console.warn( "Could not set file permissions. Please ensure .env and config.js are secured appropriately.", ); } console.log("Setup complete.");