#!/usr/bin/env node import { exec, execSync, spawnSync } from "child_process"; import { writeFileSync } from "fs"; import { env } from "process"; 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" }, "config": { type: "string", short: "c", default: "config.js" }, "write-config": { type: "string", short: "w" }, }, }); if (args.values["write-config"] && !["js", "env", "both"].includes(args.values["write-config"])) { console.error( "Invalid value for --write-config. Allowed values are: js, env, both.", ); process.exit(1); } 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); } } let envContent = `AZ_APP_NAME="${config.appName}" ARM_TENANT_ID=${config.tenantId} ARM_CLIENT_ID=${config.clientId} `; if (config.clientSecret) { envContent += `ARM_CLIENT_SECRET=${config.clientSecret}\n`; } if (["env", "both"].includes(args.values["write-config"])) { // Write the APP_ID to the .env file 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."); } else { console.log("\nThe .env file for the application:"); console.log(envContent); } if (["js", "both"].includes(args.values["write-config"])) { // Save the config to the 'config.js' file. writeFileSync( args.values["config"], `export const config = ${JSON.stringify(config, null, 4)};\n`, ); try { execSync(`chmod 600 ${args.values["config"]}`); } catch (error) { console.warn( `Could not set file permissions for ${args.values["config"]}. Please ensure it is secured appropriately.`, ); } console.log(`${args.values["config"]} file created.`); } else { console.log(`The ${args.values["config"]} file content:`); console.log( `export const config = ${JSON.stringify(config, null, 4)};\n`, ); }