using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.IO; using System.Reflection; using System.Xml; namespace WebConfigSetter { internal class Program { #region Protected Fields protected const string separatore = "---------------------------------------------------------------------------------------"; /// /// stringa di setup cliente /// protected static SetCliente currConfig = new SetCliente(); #endregion Protected Fields #region Protected Properties protected static string baseDir { get; set; } = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);//@"c:\Steamware\WebConfigSetter"; #if DEBUG protected static string baseWebAppDir { get; set; } = @"\\iis02\c$\inetpub\wwwroot\MP\"; #else protected static string baseWebAppDir { get; set; } = @"c:\inetpub\wwwroot\MP\"; #endif protected static string confDir { get; set; } = $@"{baseDir}\CONF"; protected static string currentSetFile { get; set; } = $@"{confDir}\default.json"; /// /// modalità scrittura (altrimenti legge e carica conf parsando i files) /// protected static operationMode opMode { get; set; } = operationMode.readConf; protected static string setName { get; set; } = "Steamware"; #endregion Protected Properties #region Private Methods private static void Main(string[] args) { string userInput = ""; if (args == null || args.Length == 0) { Console.WriteLine(separatore); Console.WriteLine("WebConfigSetter: Gestione configurazione config file per web applications"); Console.WriteLine(""); Console.WriteLine("Processa i file di applicazioni asp.net (XML) e dotnet core (Json), per queste ultime partendo dalla versione appsettings.Production.json"); Console.WriteLine(separatore); Console.WriteLine(""); Console.WriteLine("Sintassi: WebConfigSetter.exe $1 $2 $3"); Console.WriteLine("$1: Modalità operativa (0=read, 1=write)"); Console.WriteLine("$2: WebApp directory di base"); Console.WriteLine("$3: SetName da applicare"); Console.WriteLine(separatore); Console.WriteLine(""); Console.WriteLine("Nessuna parametro fornito, verranno ora richiesti i parametri"); Console.WriteLine(separatore); } // carico parametri opzionali if (args.Length > 0) { opMode = args[0] == "1" ? operationMode.writeConf : operationMode.readConf; } else { Console.WriteLine($"Modalità operativa 0=read, 1=write [{opMode}]"); userInput = Console.ReadLine(); if (!string.IsNullOrEmpty(userInput)) { opMode = userInput == "1" ? operationMode.writeConf : operationMode.readConf; } } if (args.Length > 1) { baseWebAppDir = args[1]; } else { Console.WriteLine($"WebApp directory di base [{baseWebAppDir}]"); userInput = Console.ReadLine(); if (!string.IsNullOrEmpty(userInput)) { baseWebAppDir = userInput; } } if (args.Length > 2) { setName = args[2]; } else { Console.WriteLine($"SetName da applicare [{setName}]"); userInput = Console.ReadLine(); if (!string.IsNullOrEmpty(userInput)) { setName = userInput; } } Console.WriteLine(separatore); Console.WriteLine($"Modalità operativa: {opMode}"); Console.WriteLine($"baseWebAppDir: {baseWebAppDir}"); Console.WriteLine($"setName: {setName}"); // salvo info... currentSetFile = $"{confDir}\\{setName}.json"; // processo richiesta processRequest(); Console.WriteLine(separatore); } /// /// Esecuzione richiesta principale /// private static void processRequest() { // caso 1: acquisisco conf if (opMode == operationMode.readConf) { readConfToFiles(); } // caso 2: effettuo sostituzioni else { writeConfToFiles(); } } private static void readConfToFiles() { // cerca i file web.config dalla folder richiesta in giù... string[] configFilesXml = Directory.GetFiles(baseWebAppDir, "web.config", SearchOption.AllDirectories); string[] configFilesJson = Directory.GetFiles(baseWebAppDir, "appsettings.Production.json", SearchOption.AllDirectories); // se fosse vuoto usa invece il file base if (configFilesJson.Length == 0) { configFilesJson = Directory.GetFiles(baseWebAppDir, "appsettings.json", SearchOption.AllDirectories); } // STEP 1: processo files XML (stile asp.net <= 4.8) XmlDocument doc = new XmlDocument(); foreach (var item in configFilesXml) { Console.WriteLine($"XML Found: {item}"); doc = new XmlDocument(); doc.Load(item); // cerco in appConfig XmlNodeList appSettingsList = doc.SelectNodes("configuration/appSettings/*"); for (int i = 0; i < appSettingsList.Count; i++) { bool doSave = false; // stringhe conn a DB principali if (appSettingsList[i].Attributes["key"].Value.Contains("ConnectionString")) { doSave = true; } // stringhe conn al DB secondari (MongoDB...) if (appSettingsList[i].Attributes["key"].Value.Contains("ConnString")) { doSave = true; } // se trovato --> salvo if (doSave) { if (!currConfig.appSettRules.ContainsKey(appSettingsList[i].Attributes["key"].Value)) { currConfig.appSettRules.Add(appSettingsList[i].Attributes["key"].Value, appSettingsList[i].Attributes["value"].Value); } } } // cerco in connectionStrings XmlNodeList connectionStringsList = doc.SelectNodes("configuration/connectionStrings/*"); for (int i = 0; i < connectionStringsList.Count; i++) { if (!currConfig.connStrRules.ContainsKey(connectionStringsList[i].Attributes["name"].Value)) { currConfig.connStrRules.Add(connectionStringsList[i].Attributes["name"].Value, connectionStringsList[i].Attributes["connectionString"].Value); } } } // STEP 2: processo files json (stile dotnetcore >= 5.0) jsonAppConfig currJsonConf = new jsonAppConfig(); foreach (var item in configFilesJson) { Console.WriteLine($"JSON Found: {item}"); string currFileData = File.ReadAllText(item); if (!string.IsNullOrEmpty(currFileData)) { try { currJsonConf = JsonConvert.DeserializeObject(currFileData); } catch (Exception exc) { Console.WriteLine($"Eccezione in decodifica item {item}{Environment.NewLine}{exc}"); } // processo le stringhe di connessione if (currJsonConf.ConnectionStrings != null) { foreach (var chiave in currJsonConf.ConnectionStrings) { if (!currConfig.jsonConnStrRules.ContainsKey(chiave.Key)) { currConfig.jsonConnStrRules.Add(chiave.Key, chiave.Value); } } } // processo area ServerConf degli altri parametri da sistemare if (currJsonConf.ServerConf != null) { foreach (var chiave in currJsonConf.ServerConf) { if (!currConfig.jsonConnStrRules.ContainsKey(chiave.Key)) { currConfig.jsonConnStrRules.Add(chiave.Key, chiave.Value); } } } } } // serializzo! string rawData = JsonConvert.SerializeObject(currConfig, Newtonsoft.Json.Formatting.Indented); // salvo il file di conf! File.WriteAllText(currentSetFile, rawData); } private static void writeConfToFiles() { // check file exists... if (File.Exists(currentSetFile)) { // leggo il file delle configurazioni da applicare... string rawData = File.ReadAllText(currentSetFile); if (!string.IsNullOrEmpty(rawData)) { currConfig = JsonConvert.DeserializeObject(rawData); // cerca i file we.config dalla folder richiesta in giù... string[] configFiles = Directory.GetFiles(baseWebAppDir, "web.config", SearchOption.AllDirectories); string[] configFilesJson = Directory.GetFiles(baseWebAppDir, "appsettings*.json", SearchOption.AllDirectories); XmlDocument doc = new XmlDocument(); // STEP 1: processo files XML (stile asp.net <= 4.8) foreach (var item in configFiles) { Console.Write($"XML Found: {item} --> "); doc = new XmlDocument(); doc.Load(item); // cerco in appConfig XmlNodeList appSettingsList = doc.SelectNodes("configuration/appSettings/*"); for (int i = 0; i < appSettingsList.Count; i++) { if (appSettingsList[i].Attributes["key"].Value.Contains("ConnectionString")) { if (currConfig.appSettRules.ContainsKey(appSettingsList[i].Attributes["key"].Value)) { // effettuo sostituzione appSettingsList[i].Attributes["value"].Value = currConfig.appSettRules[appSettingsList[i].Attributes["key"].Value]; } } } // cerco in connectionStrings XmlNodeList connectionStringsList = doc.SelectNodes("configuration/connectionStrings/*"); for (int i = 0; i < connectionStringsList.Count; i++) { if (currConfig.connStrRules.ContainsKey(connectionStringsList[i].Attributes["name"].Value)) { // effettuo sostituzione connectionStringsList[i].Attributes["connectionString"].Value = currConfig.connStrRules[connectionStringsList[i].Attributes["name"].Value]; } } // ora lo SALVO (il documento...) doc.Save(item); Console.WriteLine(" values updated!"); } // STEP 2: processo files json (stile dotnetcore >= 5.0) JToken currJsonConf; foreach (var item in configFilesJson) { Console.Write($"JSON Found: {item} --> "); string currFileData = File.ReadAllText(item); bool changed = false; if (!string.IsNullOrEmpty(currFileData)) { try { //currJsonConf = JsonConvert.DeserializeObject(currFileData); currJsonConf = JToken.Parse(currFileData); if (currJsonConf.HasValues) { if (currJsonConf["ConnectionStrings"] != null) { foreach (var keySubst in currConfig.jsonConnStrRules) { if (currJsonConf["ConnectionStrings"][keySubst.Key] != null) { currJsonConf["ConnectionStrings"][keySubst.Key].Replace(keySubst.Value); changed = true; } } } if (currJsonConf["ServerConf"] != null) { foreach (var keySubst in currConfig.jsonConnStrRules) { if (currJsonConf["ServerConf"][keySubst.Key] != null) { currJsonConf["ServerConf"][keySubst.Key].Replace(keySubst.Value); changed = true; } } } } string newFileData = JsonConvert.SerializeObject(currJsonConf, Newtonsoft.Json.Formatting.Indented); if (changed) { // ora lo SALVO (il documento...) File.WriteAllText(item, newFileData); Console.WriteLine(" values updated!"); } else { Console.WriteLine("...no change..."); } } catch (Exception exc) { Console.WriteLine($"Eccezione in decodifica item {item}{Environment.NewLine}{exc}"); } } } } else { Console.WriteLine($"Errore: file conf vuoto: {currentSetFile}"); } } else { Console.WriteLine($"Errore: file conf non trovato: {currentSetFile}"); } } #endregion Private Methods } }