public void Apply(ApplicationConfiguration configuration, ApplicationConfigurationSettings settings, string targetFile)
        {
            string contents;
            using (StreamReader reader = new StreamReader(targetFile))
            {
                contents = reader.ReadToEnd();
            }

            foreach (KeyValuePair<string, ApplicationConfigurationSetting> setting in settings.Settings)
            {
                contents = contents.Replace("{{" + setting.Key + "}}", setting.Value.Value);
            }

            using (StreamWriter writer = new StreamWriter(targetFile, false))
            {
                writer.Write(contents);
                writer.Flush();
            }
        }
        public void Apply(ApplicationConfiguration configuration, ApplicationConfigurationSettings settings, string targetFile)
        {
            XDocument cscfg;
            XNamespace ns = CscfgNamespace;

            using (StreamReader reader = new StreamReader(targetFile))
            {
                cscfg = XDocument.Load(reader);
            }

            XElement[] workerRoles = cscfg.Root.Elements(ns + "Role").ToArray();
            foreach (XElement role in workerRoles)
            {
                XElement configurationSettings = role.Element(ns + "ConfigurationSettings");
                if (configurationSettings == null)
                {
                    configurationSettings = new XElement(ns + "ConfigurationSettings");
                    role.Add(configurationSettings);
                }

                foreach (ApplicationComponent component in configuration.ApplicationComponents)
                {
                    IComponentIdentity componentIdentity = new ComponentIdentity(component.Fqn);
                    if (!string.IsNullOrWhiteSpace(component.SqlServerConnectionString))
                    {
                        string key = _nameProvider.SqlConnectionString(componentIdentity);
                        ApplyConfigSetting(configurationSettings, key, component.SqlServerConnectionString);
                    }
                    if (!string.IsNullOrWhiteSpace(component.StorageAccountConnectionString))
                    {
                        string key = _nameProvider.StorageAccountConnectionString(componentIdentity);
                        ApplyConfigSetting(configurationSettings, key, component.StorageAccountConnectionString);
                    }
                    if (!string.IsNullOrWhiteSpace(component.ServiceBusConnectionString))
                    {
                        string key = _nameProvider.ServiceBusConnectionString(componentIdentity);
                        ApplyConfigSetting(configurationSettings, key, component.ServiceBusConnectionString);
                    }
                    if (!string.IsNullOrWhiteSpace(component.DbContextType))
                    {
                        string key = _nameProvider.SqlContextType(componentIdentity);
                        ApplyConfigSetting(configurationSettings, key, component.DbContextType);
                    }
                    if (!string.IsNullOrWhiteSpace(component.DefaultQueueName))
                    {
                        string key = _nameProvider.DefaultQueueName(componentIdentity);
                        ApplyConfigSetting(configurationSettings, key, component.DefaultQueueName);
                    }
                    if (!string.IsNullOrWhiteSpace(component.DefaultBlobContainerName))
                    {
                        string key = _nameProvider.DefaultBlobContainerName(componentIdentity);
                        ApplyConfigSetting(configurationSettings, key, component.DefaultBlobContainerName);
                    }
                    if (!string.IsNullOrWhiteSpace(component.DefaultTableName))
                    {
                        string key = _nameProvider.DefaultTableName(componentIdentity);
                        ApplyConfigSetting(configurationSettings, key, component.DefaultTableName);
                    }
                    if (!string.IsNullOrWhiteSpace(component.DefaultLeaseBlockName))
                    {
                        string key = _nameProvider.DefaultLeaseBlockName(componentIdentity);
                        ApplyConfigSetting(configurationSettings, key, component.DefaultLeaseBlockName);
                    }
                    if (!string.IsNullOrWhiteSpace(component.DefaultSubscriptionName))
                    {
                        string key = _nameProvider.DefaultSubscriptionName(componentIdentity);
                        ApplyConfigSetting(configurationSettings, key, component.DefaultSubscriptionName);
                    }
                    if (!string.IsNullOrWhiteSpace(component.DefaultTopicName))
                    {
                        string key = _nameProvider.DefaultTopicName(componentIdentity);
                        ApplyConfigSetting(configurationSettings, key, component.DefaultTopicName);
                    }
                    if (!string.IsNullOrWhiteSpace(component.DefaultBrokeredMessageQueueName))
                    {
                        string key = _nameProvider.DefaultBrokeredMessageQueueName(componentIdentity);
                        ApplyConfigSetting(configurationSettings, key, component.DefaultBrokeredMessageQueueName);
                    }

                    foreach (ApplicationComponentSetting setting in component.Settings)
                    {
                        string key = _nameProvider.SettingName(componentIdentity, setting.Key);
                        ApplyConfigSetting(configurationSettings, key, setting.Value);
                    }
                }
            }

            using (FileStream outputStream = new FileStream(targetFile, FileMode.Create))
            {
                cscfg.Save(outputStream);
            }
        }
        public void Apply(ApplicationConfiguration configuration, ApplicationConfigurationSettings settings, string targetFile)
        {
            XDocument dotnetConfig;

            using (StreamReader reader = new StreamReader(targetFile))
            {
                dotnetConfig = XDocument.Load(reader);
            }
            
            XElement appSettings = dotnetConfig.Root.Element("appSettings");
            if (appSettings == null)
            {
                throw new ApplicationException("There must be an appSettings section in your .config file.");
            }
            
            foreach (ApplicationComponent component in configuration.ApplicationComponents)
            {
                IComponentIdentity componentIdentity = new ComponentIdentity(component.Fqn);
                if (!string.IsNullOrWhiteSpace(component.SqlServerConnectionString))
                {
                    string key = _nameProvider.SqlConnectionString(componentIdentity);
                    ApplyAppSetting(appSettings, key, component.SqlServerConnectionString);
                }
                if (!string.IsNullOrWhiteSpace(component.StorageAccountConnectionString))
                {
                    string key = _nameProvider.StorageAccountConnectionString(componentIdentity);
                    ApplyAppSetting(appSettings, key, component.StorageAccountConnectionString);
                }
                if (!string.IsNullOrWhiteSpace(component.ServiceBusConnectionString))
                {
                    string key = _nameProvider.ServiceBusConnectionString(componentIdentity);
                    ApplyAppSetting(appSettings, key, component.ServiceBusConnectionString);
                }
                if (!string.IsNullOrWhiteSpace(component.DbContextType))
                {
                    string key = _nameProvider.SqlContextType(componentIdentity);
                    ApplyAppSetting(appSettings, key, component.DbContextType);
                }
                if (!string.IsNullOrWhiteSpace(component.DefaultQueueName))
                {
                    string key = _nameProvider.DefaultQueueName(componentIdentity);
                    ApplyAppSetting(appSettings, key, component.DefaultQueueName);
                }
                if (!string.IsNullOrWhiteSpace(component.DefaultBlobContainerName))
                {
                    string key = _nameProvider.DefaultBlobContainerName(componentIdentity);
                    ApplyAppSetting(appSettings, key, component.DefaultBlobContainerName);
                }
                if (!string.IsNullOrWhiteSpace(component.DefaultTableName))
                {
                    string key = _nameProvider.DefaultTableName(componentIdentity);
                    ApplyAppSetting(appSettings, key, component.DefaultTableName);
                }
                if (!string.IsNullOrWhiteSpace(component.DefaultLeaseBlockName))
                {
                    string key = _nameProvider.DefaultLeaseBlockName(componentIdentity);
                    ApplyAppSetting(appSettings, key, component.DefaultLeaseBlockName);
                }
                if (!string.IsNullOrWhiteSpace(component.DefaultSubscriptionName))
                {
                    string key = _nameProvider.DefaultSubscriptionName(componentIdentity);
                    ApplyAppSetting(appSettings, key, component.DefaultSubscriptionName);
                }
                if (!string.IsNullOrWhiteSpace(component.DefaultTopicName))
                {
                    string key = _nameProvider.DefaultTopicName(componentIdentity);
                    ApplyAppSetting(appSettings, key, component.DefaultTopicName);
                }
                if (!string.IsNullOrWhiteSpace(component.DefaultBrokeredMessageQueueName))
                {
                    string key = _nameProvider.DefaultBrokeredMessageQueueName(componentIdentity);
                    ApplyAppSetting(appSettings, key, component.DefaultBrokeredMessageQueueName);
                }

                foreach (ApplicationComponentSetting setting in component.Settings)
                {
                    string key = _nameProvider.SettingName(componentIdentity, setting.Key);
                    ApplyAppSetting(appSettings, key, setting.Value);
                }

            }

            using (FileStream outputStream = new FileStream(targetFile, FileMode.Create))
            {
                dotnetConfig.Save(outputStream);
            }
            
        }
        private void ApplyCorsRules(ApplicationConfiguration configuration)
        {
            foreach (ApplicationStorageAccount storageAccount in configuration.StorageAccounts.Values)
            {
                CloudStorageAccount cloudStorageAccount = null;
                CloudTableClient tableClient = null;
                ServiceProperties tableProperties = null;
                CloudBlobClient blobClient = null;
                ServiceProperties blobProperties = null;
                CloudQueueClient queueClient = null;
                ServiceProperties queueProperties = null;
                foreach (ApplicationCorsRule rule in storageAccount.CorsRules)
                {
                    if (cloudStorageAccount == null)
                    {
                        cloudStorageAccount = CloudStorageAccount.Parse(storageAccount.ConnectionString);
                    }

                    if (rule.ApplyToTables)
                    {
                        if (tableClient == null)
                        {
                            tableClient = cloudStorageAccount.CreateCloudTableClient();
                            tableProperties = tableClient.GetServiceProperties();
                        }
                        
                        tableProperties.Cors.CorsRules.Add(CreateCorsRule(rule));
                    }

                    if (rule.ApplyToQueues)
                    {
                        if (queueClient == null)
                        {
                            queueClient = cloudStorageAccount.CreateCloudQueueClient();
                            queueProperties = queueClient.GetServiceProperties();
                        }
                        queueProperties.Cors.CorsRules.Add(CreateCorsRule(rule));
                    }

                    if (rule.ApplyToBlobs)
                    {
                        if (blobClient == null)
                        {
                            blobClient = cloudStorageAccount.CreateCloudBlobClient();
                            blobProperties = blobClient.GetServiceProperties();
                        }
                        blobProperties.Cors.CorsRules.Add(CreateCorsRule(rule));
                    }
                }

                tableClient?.SetServiceProperties(tableProperties);
                blobClient?.SetServiceProperties(blobProperties);
                queueClient?.SetServiceProperties(queueProperties);
            }
        }
        private async Task SetSecrets(ApplicationConfiguration configuration, ISecretStore secretStore)
        {
            try
            {
                IApplicationResourceSettingNameProvider nameProvider = new ApplicationResourceSettingNameProvider();
                // set the secrets
                foreach (ApplicationComponent component in configuration.ApplicationComponents)
                {
                    IComponentIdentity componentIdentity = new ComponentIdentity(component.Fqn);
                    if (!string.IsNullOrWhiteSpace(component.SqlServerConnectionString))
                    {
                        string key = nameProvider.SqlConnectionString(componentIdentity);
                        await SetValueInSecretStoreIfIsSecret(secretStore, configuration, key, component.SqlServerConnectionString);
                    }
                    if (!string.IsNullOrWhiteSpace(component.StorageAccountConnectionString))
                    {
                        string key = nameProvider.StorageAccountConnectionString(componentIdentity);
                        await SetValueInSecretStoreIfIsSecret(secretStore, configuration, key, component.StorageAccountConnectionString);
                    }
                    if (!string.IsNullOrWhiteSpace(component.ServiceBusConnectionString))
                    {
                        string key = nameProvider.ServiceBusConnectionString(componentIdentity);
                        await SetValueInSecretStoreIfIsSecret(secretStore, configuration, key, component.ServiceBusConnectionString);
                    }
                    if (!string.IsNullOrWhiteSpace(component.DbContextType))
                    {
                        string key = nameProvider.SqlContextType(componentIdentity);
                        await SetValueInSecretStoreIfIsSecret(secretStore, configuration, key, component.DbContextType);
                    }
                    if (!string.IsNullOrWhiteSpace(component.DefaultQueueName))
                    {
                        string key = nameProvider.DefaultQueueName(componentIdentity);
                        await SetValueInSecretStoreIfIsSecret(secretStore, configuration, key, component.DefaultQueueName);
                    }
                    if (!string.IsNullOrWhiteSpace(component.DefaultBlobContainerName))
                    {
                        string key = nameProvider.DefaultBlobContainerName(componentIdentity);
                        await SetValueInSecretStoreIfIsSecret(secretStore, configuration, key, component.DefaultBlobContainerName);
                    }
                    if (!string.IsNullOrWhiteSpace(component.DefaultTableName))
                    {
                        string key = nameProvider.DefaultTableName(componentIdentity);
                        await SetValueInSecretStoreIfIsSecret(secretStore, configuration, key, component.DefaultTableName);
                    }
                    if (!string.IsNullOrWhiteSpace(component.DefaultLeaseBlockName))
                    {
                        string key = nameProvider.DefaultLeaseBlockName(componentIdentity);
                        await SetValueInSecretStoreIfIsSecret(secretStore, configuration, key, component.DefaultLeaseBlockName);
                    }
                    if (!string.IsNullOrWhiteSpace(component.DefaultSubscriptionName))
                    {
                        string key = nameProvider.DefaultSubscriptionName(componentIdentity);
                        await SetValueInSecretStoreIfIsSecret(secretStore, configuration, key, component.DefaultSubscriptionName);
                    }
                    if (!string.IsNullOrWhiteSpace(component.DefaultTopicName))
                    {
                        string key = nameProvider.DefaultTopicName(componentIdentity);
                        await SetValueInSecretStoreIfIsSecret(secretStore, configuration, key, component.DefaultTopicName);
                    }
                    if (!string.IsNullOrWhiteSpace(component.DefaultBrokeredMessageQueueName))
                    {
                        string key = nameProvider.DefaultBrokeredMessageQueueName(componentIdentity);
                        await SetValueInSecretStoreIfIsSecret(secretStore, configuration, key, component.DefaultBrokeredMessageQueueName);
                    }

                    foreach (ApplicationComponentSetting setting in component.Settings)
                    {
                        string key = nameProvider.SettingName(componentIdentity, setting.Key);
                        await SetValueInSecretStoreIfIsSecret(secretStore, configuration, key, setting.Value);
                    }
                }
            }
            catch (AggregateException aex)
            {
                foreach (Exception ex in aex.InnerExceptions)
                {
                    WriteError(new ErrorRecord(ex, Guid.NewGuid().ToString(), ErrorCategory.InvalidOperation, null));
                }
            }
            
        }
 private async Task SetValueInSecretStoreIfIsSecret(ISecretStore secretStore, ApplicationConfiguration configuration, string key, string value)
 {
     if (configuration.Secrets.Contains(value))
     {
         //WriteVerbose($"Saving key {key} to key vault encoded as encoded key {secretStore.EncodeKey(key)}");
         await secretStore.Save(key, value);
     }
 }
        private static async Task SetSecrets(ApplicationConfiguration configuration, IKeyVault secretStore, IKeyVaultConfigurationKeyEncoder encoder, bool verbose)
        {
            try
            {
                IApplicationResourceSettingNameProvider nameProvider = new ApplicationResourceSettingNameProvider();
                // set the secrets
                foreach (ApplicationComponent component in configuration.ApplicationComponents)
                {
                    IComponentIdentity componentIdentity = new ComponentIdentity(component.Fqn);
                    if (!string.IsNullOrWhiteSpace(component.SqlServerConnectionString))
                    {
                        string key = nameProvider.SqlConnectionString(componentIdentity);
                        await SetValueInSecretStoreIfIsSecret(secretStore, encoder, verbose, configuration, key, component.SqlServerConnectionString);
                    }
                    if (!string.IsNullOrWhiteSpace(component.StorageAccountConnectionString))
                    {
                        string key = nameProvider.StorageAccountConnectionString(componentIdentity);
                        await SetValueInSecretStoreIfIsSecret(secretStore, encoder, verbose, configuration, key, component.StorageAccountConnectionString);
                    }
                    if (!string.IsNullOrWhiteSpace(component.ServiceBusConnectionString))
                    {
                        string key = nameProvider.ServiceBusConnectionString(componentIdentity);
                        await SetValueInSecretStoreIfIsSecret(secretStore, encoder, verbose, configuration, key, component.ServiceBusConnectionString);
                    }
                    if (!string.IsNullOrWhiteSpace(component.DbContextType))
                    {
                        string key = nameProvider.SqlContextType(componentIdentity);
                        await SetValueInSecretStoreIfIsSecret(secretStore, encoder, verbose, configuration, key, component.DbContextType);
                    }
                    if (!string.IsNullOrWhiteSpace(component.DefaultQueueName))
                    {
                        string key = nameProvider.DefaultQueueName(componentIdentity);
                        await SetValueInSecretStoreIfIsSecret(secretStore, encoder, verbose, configuration, key, component.DefaultQueueName);
                    }
                    if (!string.IsNullOrWhiteSpace(component.DefaultBlobContainerName))
                    {
                        string key = nameProvider.DefaultBlobContainerName(componentIdentity);
                        await SetValueInSecretStoreIfIsSecret(secretStore, encoder, verbose, configuration, key, component.DefaultBlobContainerName);
                    }
                    if (!string.IsNullOrWhiteSpace(component.DefaultTableName))
                    {
                        string key = nameProvider.DefaultTableName(componentIdentity);
                        await SetValueInSecretStoreIfIsSecret(secretStore, encoder, verbose, configuration, key, component.DefaultTableName);
                    }
                    if (!string.IsNullOrWhiteSpace(component.DefaultLeaseBlockName))
                    {
                        string key = nameProvider.DefaultLeaseBlockName(componentIdentity);
                        await SetValueInSecretStoreIfIsSecret(secretStore, encoder, verbose, configuration, key, component.DefaultLeaseBlockName);
                    }
                    if (!string.IsNullOrWhiteSpace(component.DefaultSubscriptionName))
                    {
                        string key = nameProvider.DefaultSubscriptionName(componentIdentity);
                        await SetValueInSecretStoreIfIsSecret(secretStore, encoder, verbose, configuration, key, component.DefaultSubscriptionName);
                    }
                    if (!string.IsNullOrWhiteSpace(component.DefaultTopicName))
                    {
                        string key = nameProvider.DefaultTopicName(componentIdentity);
                        await SetValueInSecretStoreIfIsSecret(secretStore, encoder, verbose, configuration, key, component.DefaultTopicName);
                    }
                    if (!string.IsNullOrWhiteSpace(component.DefaultBrokeredMessageQueueName))
                    {
                        string key = nameProvider.DefaultBrokeredMessageQueueName(componentIdentity);
                        await SetValueInSecretStoreIfIsSecret(secretStore, encoder, verbose, configuration, key, component.DefaultBrokeredMessageQueueName);
                    }

                    foreach (ApplicationComponentSetting setting in component.Settings)
                    {
                        string key = nameProvider.SettingName(componentIdentity, setting.Key);
                        await SetValueInSecretStoreIfIsSecret(secretStore, encoder, verbose, configuration, key, setting.Value);
                    }
                }
            }
            catch (AggregateException aex)
            {
                StringBuilder sb = new StringBuilder();
                foreach (Exception ex in aex.InnerExceptions)
                {
                    sb.AppendLine(ex.Message);                    
                }
                throw new ConsoleAppException(sb.ToString(), aex);
            }

        }
        private static async Task SetValueInSecretStoreIfIsSecret(IKeyVault secretStore, IKeyVaultConfigurationKeyEncoder encoder, bool verbose, ApplicationConfiguration configuration, string key, string value)
        {
            if (configuration.Secrets.Contains(value))
            {
                string encodedKey = encoder.Encode(key);
                await secretStore.SetSecretAsync(encodedKey, value);

                if (verbose)
                {
                    Console.ForegroundColor = ConsoleColor.Green;
                    Console.WriteLine("Secret set for key {0}", key);
                }
            }
        }
 private void ApplyAppSetting(ApplicationConfiguration configuration, XElement appSettings, string key, string value)
 {
     string xpath = $"add[@key='{key}']";
     XElement appSetting = appSettings.XPathSelectElement(xpath);
     if (configuration.Secrets.Contains(value))
     {
         appSetting?.Remove();
     }
     else
     {                
         if (appSetting == null)
         {
             appSetting = new XElement("add", new XAttribute("key", key), new XAttribute("value", value));
             appSettings.Add(appSetting);
         }
         else
         {
             appSetting.SetAttributeValue("value", value);
         }
     }
 }
        /// <summary>
        /// Loads the application configuration from an XML document
        /// </summary>
        /// <param name="document">The document</param>
        /// <param name="settings">An optional settings file</param>
        /// <param name="checkForMissingSettings">If set to true then any missing settings generate an exception</param>
        /// <param name="applicationSecretStore">Optional secret store to use in addition to the settings</param>
        /// <param name="verboseLogger">Optional verbose logger</param>
        /// <returns>An application configuration</returns>
        public static async Task<ApplicationConfiguration> FromXDocumentAsync(XDocument document, ApplicationConfigurationSettings settings,
            bool checkForMissingSettings, IAsyncConfiguration applicationSecretStore = null, Action<string> verboseLogger = null)
        {
            if (document.Root == null) return null;
            HashSet<string> secrets = new HashSet<string>();
            
            verboseLogger?.Invoke("Processing settings");
            ApplicationConfiguration configuration = new ApplicationConfiguration();
            IApplicationResourceSettingNameProvider nameProvider = new ApplicationResourceSettingNameProvider();
            IEnumerable<XElement> allDescendants = document.Descendants();
            Regex settingPattern = new Regex(@"(?:\{\{)([^}]*)(?:\}\})");
            foreach (XElement element in allDescendants)
            {
                if (!element.HasElements)
                {
                    Match match = settingPattern.Match(element.Value);
                    string value = element.Value;
                    bool containsSecret = false;
                    while (match.Success)
                    {
                        string settingName = match.Groups[1].Value;
                        ApplicationConfigurationSetting setting;
                        if (!settings.Settings.TryGetValue(settingName, out setting) && checkForMissingSettings)
                        {
                            throw new MissingSettingException();
                        }
                        if (setting != null)
                        {
                            containsSecret |= setting.IsSecret;
                            value = value.Replace($"{{{{{settingName}}}}}", setting.Value);
                        }
                        match = match.NextMatch();
                    }
                    element.Value = value;
                    if (containsSecret)
                    {
                        secrets.Add(value);
                    }
                }
            }
            
            document.Root.XPathSelectElements("infrastructure/sql-server").ToList().ForEach(element =>
            {
                var xfqn = element.Element("fqn");
                if (xfqn != null)
                {
                    var xconnectionstring = element.Element("connection-string");
                    if (xconnectionstring != null)
                        configuration.SqlServerConnectionStrings.Add(xfqn.Value, xconnectionstring.Value);
                }
            });
            document.Root.XPathSelectElements("infrastructure/storage-account").ToList().ForEach(element =>
            {
                ApplicationStorageAccount storageAccount = new ApplicationStorageAccount(element);
                configuration.StorageAccounts.Add(storageAccount.Fqn, storageAccount);
            });
            document.Root.XPathSelectElements("infrastructure/service-bus").ToList().ForEach(element =>
            {
                var xfqn = element.Element("fqn");
                if (xfqn != null)
                {
                    var xconnectionstring = element.Element("connection-string");
                    if (xconnectionstring != null)
                        configuration.ServiceBusConnectionStrings.Add(xfqn.Value, xconnectionstring.Value);
                }
            });

            foreach (XElement element in document.Root.Elements("component").ToList())
            {
                ApplicationComponent component = new ApplicationComponent
                {
                    Fqn = element.Attribute("fqn").Value
                };
                IComponentIdentity componentIdentity = new ComponentIdentity(component.Fqn);
                verboseLogger?.Invoke($"Parsing component {componentIdentity}");

                XElement sqlServerElement = element.Element("sql-server");
                XElement storageElement = element.Element("storage-account");
                XElement serviceBusElement = element.Element("service-bus");
                XElement dbContextTypeElement = element.Element("db-context-type");
                XElement defaultBlobContainerNameElement = element.Element("default-blob-container-name");
                XElement defaultQueueNameElement = element.Element("default-queue-name");
                XElement defaultTableNameElement = element.Element("default-table-name");
                XElement defaultTableData = element.Element("table-data");
                XElement defaultLeaseBlockNameElement = element.Element("default-lease-block-name");
                XElement defaultSubscriptionNameElement = element.Element("default-subscription-name");
                XElement defaultTopicNameElement = element.Element("default-topic-name");
                XElement defaultBrokeredMessageQueueNameElement = element.Element("default-brokered-message-queue-name");
                XElement settingsElement = element.Element("settings");
                XAttribute defaultBlobContainerAccessAttribute = defaultBlobContainerNameElement?.Attribute("public-permission");

                
                if (sqlServerElement != null)
                {
                    try
                    {
                        string secret = applicationSecretStore != null ? await applicationSecretStore.GetAsync(nameProvider.SqlConnectionString(componentIdentity)) : null;
                        component.SqlServerConnectionString = secret ?? configuration.SqlServerConnectionStrings[sqlServerElement.Value];
                    }
                    catch (KeyNotFoundException)
                    {
                        throw new InvalidDataException($"Sql server with fqn of {sqlServerElement.Value} is missing from configuration file.");
                    }
                    
                }
                if (storageElement != null)
                {
                    try
                    {
                        string secret = applicationSecretStore != null ? await applicationSecretStore.GetAsync(nameProvider.StorageAccountConnectionString(componentIdentity)) : null;
                        component.StorageAccountConnectionString = secret ?? configuration.StorageAccounts[storageElement.Value].ConnectionString;
                    }
                    catch (Exception)
                    {
                        throw new InvalidDataException($"Storage account with fqn of {storageElement.Value} is missing from configuration file.");
                    }
                }
                if (serviceBusElement != null)
                {
                    try
                    {
                        verboseLogger?.Invoke($"Looking for service bus connection string for component {componentIdentity}");
                        string secret = applicationSecretStore != null ? await applicationSecretStore.GetAsync(nameProvider.ServiceBusConnectionString(componentIdentity)) : null;                        
                        if (secret != null)
                        {
                            verboseLogger?.Invoke($"Using secret store service bus connection string for component {componentIdentity}");
                        }
                        component.ServiceBusConnectionString = secret ?? configuration.ServiceBusConnectionStrings[serviceBusElement.Value];
                    }
                    catch (Exception)
                    {
                        throw new InvalidDataException($"Service bus account with fqn of {serviceBusElement.Value} is missing from configuration file.");
                    }
                }

                string name = nameProvider.SqlContextType(componentIdentity);
                verboseLogger?.Invoke(name);
                string dbContextTypeSecret = applicationSecretStore != null ? await applicationSecretStore.GetAsync(name) : null;
                if (!string.IsNullOrWhiteSpace(dbContextTypeSecret)) verboseLogger?.Invoke($"Using secret store for dbContextType for component {componentIdentity}");
                component.DbContextType = dbContextTypeSecret ?? dbContextTypeElement?.Value;

                string defaultBlobContainerNameSecret = applicationSecretStore != null ? await applicationSecretStore.GetAsync(nameProvider.DefaultBlobContainerName(componentIdentity)) : null;
                if (!string.IsNullOrWhiteSpace(defaultBlobContainerNameSecret)) verboseLogger?.Invoke($"Using secret store for default blob container name for component {componentIdentity}");
                component.DefaultBlobContainerName = defaultBlobContainerNameSecret ?? defaultBlobContainerNameElement?.Value;

                string defaultQueueNameSecret = applicationSecretStore != null ? await applicationSecretStore.GetAsync(nameProvider.DefaultQueueName(componentIdentity)) : null;
                if (!string.IsNullOrWhiteSpace(defaultQueueNameSecret)) verboseLogger?.Invoke($"Using secret store for default queue name for component {componentIdentity}");
                component.DefaultQueueName = defaultQueueNameSecret ?? defaultQueueNameElement?.Value;

                string defaultTableNameSecret = applicationSecretStore != null ? await applicationSecretStore.GetAsync(nameProvider.DefaultTableName(componentIdentity)) : null;
                if (!string.IsNullOrWhiteSpace(defaultTableNameSecret)) verboseLogger?.Invoke($"Using secret store for default table name for component {componentIdentity}");
                component.DefaultTableName = defaultTableNameSecret ?? defaultTableNameElement?.Value;

                component.DefaultBlobContainerAccessType = BlobContainerPublicAccessTypeEnum.Off;

                string defaultLeaseBlockNameSecret = applicationSecretStore != null ? await applicationSecretStore.GetAsync(nameProvider.DefaultLeaseBlockName(componentIdentity)) : null;
                if (!string.IsNullOrWhiteSpace(defaultLeaseBlockNameSecret)) verboseLogger?.Invoke($"Using secret store for default lease block name for component {componentIdentity}");
                component.DefaultLeaseBlockName = defaultLeaseBlockNameSecret ?? defaultLeaseBlockNameElement?.Value;

                string defaultTopicNameSecret = applicationSecretStore != null ? await applicationSecretStore.GetAsync(nameProvider.DefaultTopicName(componentIdentity)) : null;
                if (!string.IsNullOrWhiteSpace(defaultTopicNameSecret)) verboseLogger?.Invoke($"Using secret store for default topic name for component {componentIdentity}");
                component.DefaultTopicName = defaultTopicNameSecret ?? defaultTopicNameElement?.Value;

                string defaultSubscriptionNameSecret = applicationSecretStore != null ? await applicationSecretStore.GetAsync(nameProvider.DefaultSubscriptionName(componentIdentity)) : null;
                if (!string.IsNullOrWhiteSpace(defaultSubscriptionNameSecret)) verboseLogger?.Invoke($"Using secret store for default subscription name for component {componentIdentity}");
                component.DefaultSubscriptionName = defaultSubscriptionNameSecret ?? defaultSubscriptionNameElement?.Value;

                string defaultBrokeredMessageQueueNameSecret = applicationSecretStore != null ? await applicationSecretStore.GetAsync(nameProvider.DefaultBrokeredMessageQueueName(componentIdentity)) : null;
                if (!string.IsNullOrWhiteSpace(defaultBrokeredMessageQueueNameSecret)) verboseLogger?.Invoke($"Using secret store for default brokered message queue name for component {componentIdentity}");
                component.DefaultBrokeredMessageQueueName = defaultBrokeredMessageQueueNameSecret ?? defaultBrokeredMessageQueueNameElement?.Value;

                component.TableData = defaultTableData?.Value;
                component.Uploads = element.Elements("upload").Select(x => x.Value).ToList();
                if (defaultBlobContainerAccessAttribute != null)
                {
                    string accessAttribtueValue = defaultBlobContainerAccessAttribute.Value.ToLower();
                    if (accessAttribtueValue == "blob")
                    {
                        component.DefaultBlobContainerAccessType = BlobContainerPublicAccessTypeEnum.Blob;
                    }
                    else if (accessAttribtueValue == "container")
                    {
                        component.DefaultBlobContainerAccessType = BlobContainerPublicAccessTypeEnum.Container;
                    }
                }

                verboseLogger?.Invoke($"Processing settings for {componentIdentity}");
                if (settingsElement != null)
                {
                    foreach (XElement componentSettingsElement in settingsElement.Elements().ToList())
                    {
                        string resourceType = null;
                        XAttribute resourceTypeAttr = componentSettingsElement.Attribute("resource-type");
                        if (resourceTypeAttr != null)
                        {
                            resourceType = resourceTypeAttr.Value;
                        }
                        Dictionary<string, string> attributeDictionary = componentSettingsElement.Attributes().ToDictionary(attribute => attribute.Name.LocalName, attribute => attribute.Value);
                        component.Settings.Add(new ApplicationComponentSetting
                        {
                            Key = componentSettingsElement.Name.LocalName,
                            ResourceType = resourceType,
                            Value = (applicationSecretStore != null ? await applicationSecretStore.GetAsync(nameProvider.SettingName(componentIdentity, componentSettingsElement.Name.LocalName)) : null) ?? componentSettingsElement.Value,
                            Attributes = attributeDictionary
                        });
                    }
                }
                
                configuration.ApplicationComponents.Add(component);
                verboseLogger?.Invoke($"Finished parsing component {componentIdentity}");
            }

            configuration.Secrets = secrets.ToList();

            return configuration;
        }
        public static ApplicationConfiguration FromFile(string filename, ApplicationConfigurationSettings settings)
        {
            ApplicationConfiguration configuration = new ApplicationConfiguration();
            XDocument document;
            using (StreamReader reader = new StreamReader(filename))
            {
                if (settings != null)
                {
                    string processedXml = settings.Merge(reader);
                    document = XDocument.Parse(processedXml);
                }
                else
                {
                    document = XDocument.Load(reader);
                }
            }

            document.Root.XPathSelectElements("infrastructure/sql-server").ToList().ForEach(element =>
            {
                configuration.SqlServerConnectionStrings.Add(element.Element("fqn").Value, element.Element("connection-string").Value);
            });
            document.Root.XPathSelectElements("infrastructure/storage-account").ToList().ForEach(element =>
            {
                ApplicationStorageAccount storageAccount = new ApplicationStorageAccount(element);
                configuration.StorageAccounts.Add(storageAccount.Fqn, storageAccount);
            });
            document.Root.XPathSelectElements("infrastructure/service-bus").ToList().ForEach(element =>
            {
                configuration.ServiceBusConnectionStrings.Add(element.Element("fqn").Value, element.Element("connection-string").Value);
            });

            document.Root.Elements("component").ToList().ForEach(element =>
            {
                ApplicationComponent component = new ApplicationComponent
                {
                    Fqn = element.Attribute("fqn").Value
                };
                XElement sqlServerElement = element.Element("sql-server");
                XElement storageElement = element.Element("storage-account");
                XElement serviceBusElement = element.Element("service-bus");
                XElement dbContextTypeElement = element.Element("db-context-type");
                XElement defaultBlobContainerNameElement = element.Element("default-blob-container-name");
                XElement defaultQueueNameElement = element.Element("default-queue-name");
                XElement defaultTableNameElement = element.Element("default-table-name");
                XElement defaultTableData = element.Element("table-data");
                XElement defaultLeaseBlockNameElement = element.Element("default-lease-block-name");
                XElement defaultSubscriptionNameElement = element.Element("default-subscription-name");
                XElement defaultTopicNameElement = element.Element("default-topic-name");
                XElement defaultBrokeredMessageQueueNameElement = element.Element("default-brokered-message-queue-name");
                XElement settingsElement = element.Element("settings");
                XAttribute defaultBlobContainerAccessAttribute = defaultBlobContainerNameElement?.Attribute("public-permission");

                if (sqlServerElement != null)
                {
                    try
                    {
                        component.SqlServerConnectionString = configuration.SqlServerConnectionStrings[sqlServerElement.Value]; component.SqlServerConnectionString = configuration.SqlServerConnectionStrings[sqlServerElement.Value];
                    }
                    catch (KeyNotFoundException)
                    {
                        throw new InvalidDataException($"Sql server with fqn of {sqlServerElement.Value} is missing from configuration file.");
                    }
                    
                }
                if (storageElement != null)
                {
                    try
                    {
                        component.StorageAccountConnectionString = configuration.StorageAccounts[storageElement.Value].ConnectionString;
                    }
                    catch (Exception)
                    {
                        throw new InvalidDataException($"Storage account with fqn of {storageElement.Value} is missing from configuration file.");
                    }
                }
                if (serviceBusElement != null)
                {
                    try
                    {
                        component.ServiceBusConnectionString = configuration.ServiceBusConnectionStrings[serviceBusElement.Value];
                    }
                    catch (Exception)
                    {
                        throw new InvalidDataException($"Service bus account with fqn of {serviceBusElement.Value} is missing from configuration file.");
                    }
                }

                component.DbContextType = dbContextTypeElement?.Value;
                component.DefaultBlobContainerName = defaultBlobContainerNameElement?.Value;
                component.DefaultQueueName = defaultQueueNameElement?.Value;
                component.DefaultTableName = defaultTableNameElement?.Value;
                component.DefaultBlobContainerAccessType = BlobContainerPublicAccessTypeEnum.Off;
                component.DefaultLeaseBlockName = defaultLeaseBlockNameElement?.Value;
                component.DefaultTopicName = defaultTopicNameElement?.Value;
                component.DefaultSubscriptionName = defaultSubscriptionNameElement?.Value;
                component.DefaultBrokeredMessageQueueName = defaultBrokeredMessageQueueNameElement?.Value;
                component.TableData = defaultTableData?.Value;
                component.Uploads = element.Elements("upload").Select(x => x.Value).ToList();
                if (defaultBlobContainerAccessAttribute != null)
                {
                    string accessAttribtueValue = defaultBlobContainerAccessAttribute.Value.ToLower();
                    if (accessAttribtueValue == "blob")
                    {
                        component.DefaultBlobContainerAccessType = BlobContainerPublicAccessTypeEnum.Blob;
                    }
                    else if (accessAttribtueValue == "container")
                    {
                        component.DefaultBlobContainerAccessType = BlobContainerPublicAccessTypeEnum.Container;
                    }
                }

                settingsElement?.Elements().ToList().ForEach(x =>
                {
                    string resourceType = null;
                    XAttribute resourceTypeAttr = x.Attribute("resource-type");
                    if (resourceTypeAttr != null)
                    {
                        resourceType = resourceTypeAttr.Value;
                    }
                    Dictionary<string, string> attributeDictionary = x.Attributes().ToDictionary(attribute => attribute.Name.LocalName, attribute => attribute.Value);
                    component.Settings.Add(new ApplicationComponentSetting
                    {
                        Key = x.Name.LocalName,
                        ResourceType = resourceType,
                        Value = x.Value,
                        Attributes = attributeDictionary
                    });                        
                });

                configuration.ApplicationComponents.Add(component);
            });

            return configuration;
        }