#!/usr/bin/env node import { exec, 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" }, "write-config": { type: "boolean", short: "w" }, "write-env": { type: "boolean", short: "e" }, }, }); 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.clientId = appIdList[0].appId; } catch (error) { console.error("Failed to query Azure AD applications."); process.exit(1); } if (config.clientId) { console.log(`App already exists with Client ID: ${config.clientId}`); } else { // Create the Azure AD application, capturing the JSON output, and extracting the .appId try { config.clientId = 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 Client ID: ${config.clientId}`); } // Check if service principal exists let spObjId; try { const spIdList = JSON.parse( execSync(`az ad sp list --filter "appId eq '${config.clientId}'" -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.clientId} --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.clientId, "--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); } } if (args.values["write-env"] || args.values["generate-client-secret"]) { // Write the APP_ID to the .env file const envContent = `AZ_APP_NAME="${config.appName}" ARM_TENANT_ID=${config.tenantId} ARM_CLIENT_ID=${config.clientId} ARM_CLIENT_SECRET=${config.clientSecret || ""} `; writeFileSync(".env", envContent); try { execSync("chmod 600 .env"); } catch (error) { console.warn( "Could not set file permissions for .env. Please ensure it is secured appropriately.", ); } console.log(".env file created with application configuration."); } if (args.values["write-config"] || args.values["generate-client-secret"]) { // Save the config to the 'config.js' file. writeFileSync( "config.js", `export const config = ${JSON.stringify(config, null, 4)};\n`, ); try { execSync("chmod 600 config.js"); } catch (error) { console.warn( "Could not set file permissions for config.js. Please ensure it is secured appropriately.", ); } console.log("config.js file created."); } console.log("Setup complete.");