I have a solution with about 10 projects with read-only config. They are web applications, windows services, console apps, etc. All projects except for one are on the same server. Each project has 3 environments - dev, test, and production. So there are 30 different sets of configuration, each one with a decent number of settings. It's cumbersome to keep the config consistent across every app and environment.
I've noticed most of the configuration is common across each project, so I was thinking it would be good to centralize the config in some way. I read somewhere that a WCF service might be a good approach. I thought maybe a library containing a hard-coded static class might actually work OK - despite having to compile to change config. Ideally the config should come out of an actual .config file.
How would you go about centralizing config for multiple projects?
If you want to maintain the standard configuration interface, take a look at the ProtectedConfigurationProvider. This provider lets you store your configuration data outside of a standard configuration file, encrypt it however you like, or redirect requests for configuration in any way you see fit:
Redirecting Configuration with a Custom Provider - Wrox
Implementing a Protected Configuration Provider - MSDN
Protected Configuration - Blayd Software
The beauty of this approach is that nothing changes in your existing applications. They don't need to know where their configuration is stored. The retrieval of configuration data is isolated in the provider. You can store it in a central file, store it in a database, or access it via a web service. If you change your mind, you only have to update your provider. Everything else stays the same.
You could certainly set up a WCF service that has a simple operation to retrieve configuration settings, taking in the application and environment as a parameter; you could then have the service load up the correct config from a file and return it to the caller. It might be a good idea to do nested configuration files, so that common settings are only defined once at their most generic level.
A potential issue could arise if the WCF service is down when starting up one of your apps -- you would need to decide if there is default config/caching of the previous copy for this situation, or if you just don't allow apps to start up if they cannot connect.
Another thing to consider, though, is the benefit of .config files in .NET in that when they change the app can respond; you may want to have a callback WCF service that notifies clients if their configuration has been updated on the central server, so they can request a new copy and update themselves if necessary.
Since they are (almost) all on the same server you could consider providing defaults in the machine.config and/or central web.config files. I'm not normally a fan of using/changing these file but they are there... in \Windows\Micsrosoft.NET\Framework<version>\Config\
You can use a centralized configuration sever like Lygeum which permits to manage applications and environments through web console or Command Line Interface with user management and client management module, clients may be in your case web apps, console services or whatever. The server installation is simple through docker.
Related
I have a requirement to configure IIS for more than one client that will use a single codebase but different application pools and databases. For example, let's assume that the base URL of my website is https://www.mywebsite.com/ and I have five clients Client1, Client2, Client3, Client4, Client5. So the URLs should be the following: https://www.mywebsite.com/Client1, https://www.mywebsite.com/Client2, https://www.mywebsite.com/Client3, https://www.mywebsite.com/Client4, https://www.mywebsite.com/Client5, and every client must have its own application pool and database but they should use the same codebase.
Is there a possibility to configure IIS based on the above requirement?
I Suggest you to create an image from your codebase and use Docker to solve your problem.
You can move all of your database settings to config file and change them easily for each image.
Here is a tutorial of working with docker:
https://www.scalyr.com/blog/create-docker-image/
Quick version of the question:
How do I configure a .Net Core windows service to target the correct database in a multi-tenant environment where each tenant has their own database and they all run on the same self-hosted server?
Some background info:
I am working on a new windows service and, since it is completely new, we are going to use .Net Core. I have read this page and it does talk about how to set environment variable in an IIS app, azure, globally, per command window but it does not really mention a windows service, azure devops or how to handle a multi-tenant environment.
The best I can figure is that you are supposed to set the environment variable in the start parameters for the windows service once it is created but that seems very fragile. This becomes more of a problem when you are looking at 25 with a potential of 100 or more (we are a small growing company). The thought of having to go back and set all of these variables manually if we decide to migrate services to another server is not pleasant.
So the longer version of the question is: Am I on the correct track or is there some better way to set this up so that this does not become such a manual process? Perhaps setting a variable when the service is deployed to the server would do the trick? How would I do that with azure devops though?
Edit 1
Here is a representation of the environment we are running these services in.
Databases (separate machine):
Shared1
Db1
Db2
Db3
Machines:
Server1
Windows Services:
Service1
Service2
Service3
The databases are running on the db server. There is one database per service and there is a shared database which stores some common information (don't think this is relevant for the question). There is one server. On that one server there are copies of the code that needs to run as a windows service. The number of copies of the service corresponds to the number of databases. So for the above scenario: Serivce1 connects to Db1, Service2 connects to Db2, Service3 connects to Db3, etc...
Since I only have one machine, if I set an environment variable to something like ASPNETCORE_DB1 then all three services will read that variable and connect to Db1 which is not what needs to happen.
If I set multiple environment variables: ASPNETCORE_Db1, ASPNETCORE_Db2, ASPNETCORE_Db2, how do each of the services know which environment variable to read?
I have a feeling that you are mixing few things here.
If you want to have different settings per environment you just need to use CreateHostBuilder method. You can read about this here. Then you need set ASPNETCORE_ENVIRONMENT environment variable on machine where you host app. In this approach application at runtime selects proper configuration. However, please do not keep sensitive data in your config files. In this approach you have one compiled code for all tenants.
You can pass configuration when you build an app. To do that you should define variable groups (on Azure DevOps) (one per tenat) and flush your secrets to config file. In addition you can use runtime parameters to define target tenant. Still not secure enough as in artifact (compiled code) you can find secrets. In this approach you have one compiled code per tenant.
If you can use Azure KeyVault I would recommend this approch. You can create one KeyVault per tenat and then in CreateHostBuilder load secrets from proper KeyVault (if you name KeyVault with tenant name, it will simplify selecting proper KeyVault). You can read about this here.
According to you question I assumed that your tenat = envrionemnt. If not then you can have one KeyVault per env like Dev, QA and PROD. Then you should name your secrets with following pattern {tenant_name}_{secret_name}. You should load all secrets atarting app and then at runtime (based on tenant id) select proper secret.
I hope it will help you make good decision.
Getting corresponding database connectionstring per environment variable is the better way. Simple steps:
Define the same environment variable with different value (connectionstring) in each environment/machine manually
Get that environment variable value in your windows service app and use that value as database connectionstring if not null, otherwise use the default connectionstring.
Edit:
For multiple windows services on the same machine, I suggest that you could specify connectionstring per to current service name.
How can a Windows Service determine its ServiceName?
As the title suggests I'd like to create a single Web Site in IIS that creates multiple instances of an ASP.net application based on the requested Host.
So that all instances are running the same codebase, but each instance has it's own Application object, Session's collection etc.
For example :
host1.domain.tld/default.aspx -> this.Application["foo"] = "host1"
host2.domain.tld/default.aspx -> this.Application["foo"] = "host2"
host3.domain.tld/default.aspx -> this.Application["foo"] = "host3"
I know I can configure IIS to listen to a specific IP address, and set the DNS for host(1|2|3).domain.tld to point at this IP address. Then use Global.asax to check the requested host to setup host specific settings. But the application will still be running as a single instance on the server.
I'd rather have multiple instances of the application running for each host so that their runtime is fully separated, it would also be nice if I could have them in separate Application Pools too, but that's not so important
Of course, I could add the sites individually into the IIS on the servers, but there are some 1600 instances that will need to be configured and this will be very time consuming to do and difficult to manage.
Being able to setup a single instances on a number of servers, then control the load balancing via DNS configuration or filtering on the firewalls both of which can be easily controlled programmatically.
FYI - the asp.net version in use is 4.0 and the IIS is running on a Windows Server 2008.
Any suggestions would be great.
Many thanks
The simplest and most robust way to do this would be to setup individual IIS sites. I know you don't want to do this because it will be very time consuming and definately would be difficult to manage.
However, you've already created a website so now perhaps it's time to create a management tool for instances of that website. As there are 1600 instances that you want to configure, there's a fairly good chance that you already have details of those 1600 instances stored somewhere, such as a database or a spreadsheet.
So:
Get the data about the 1600 instances into a usable format, a Sql Server database (Express, or paid for!) would probably be ideal.
Investigate the IIS7 provisioning APIs
Put together a tool that allows you to create all 1600 instances from the data you have about them, automatically / in batches, via the IIS7 API.
Maintain tool and expand it ready for the inevitable changes that will be required when you need to add or remove instances.
Don't forget that putting your own tool together for a task such as this gives you a lot of flexibility, although there may be tools out there for this purpose that are worthy of investigation. For that (i.e. a non-programmatic solution), I'd suggest asking at http://www.serverfault.com
I am working on a .NET Windows Service where I am trying to store settings that will be used when the service is started and while it is running. I have searched through posts on SO and found that using the Settings in the properties of the project is great for use with console and winforms applications. However, Google and SO are silent when it pertains to storing these settings with a windows service.
Does anyone one know if it is proper to use these settings in a .NET service? If not, is Serialization my next best choice? Has anyone had practical uses for settings in a service and found that it is best to use a specific method?
I've had problems with using Settings.settings. For instance, if you need to make changes at run-time, there can be problems with the settings being overridden by those that were initially stored in the settings.settings file as opposed to what's shown should be stored per the app/web.config. Consequently I make all my Web Service proxy settings "static" in the properties and pull them manually from the app/web.config via a helper method and programmatically set them. This circumvents any problems.
An example of the issue we had: I pointed my development machine to a web service on a test server to test the code that consumed the web service. When the code was moved to our test server, no problems manifested - as the test server was still pointed at the same web service on the same test server. However, when we moved the application to the production server and reconfigured the web.config to point at the production server we started getting screwy results. It took quite a bit of effort to pinpoint that even though we had reconfigured the application to point at the production server's implementation of the web service, it was still connecting to the web service on the test server. It wasn't until we changed settings.settings on my development machine and recompiled the application that it worked. Further to this, we also noted that if there were DNS problems connecting to the production web service, rather than fail, it fell back to the original settings that were specified in the settings.settings from when we created the web service proxy in our application - the proxy generator actually hard codes them. Consequently when there were network outages, instead of getting easily diagnosed connection failures, it simply fell back to the test server and we started getting incomprehensible data issues. I'm not sure if this was a known problem or if it's been fixed, but it's certainly something you should be aware of.
Consequently, since then, I've always set the service properties to static and used a helper method to read the correct settings from the web.config directly and written them programmatically as this seems to circumvent the problem.
It may seem like the problem I had has nothing to do with yours because I was using Web Services which isn't anything to do with Windows Services, however, any environment where you need to be able to change the settings at runtime without having to recompile could be affected by this issue, so you should be aware that if you run in a Dev/Test/Production environment or indeed any environment where you need your app to be reconfigured at run-time (i.e. without having to recompile) that you can get unpredictable results when using settings.settings. Beware.
I normally use the registry to store information I need in my service i.e port etc.
string lsbkey = #"Software\mycompany\adas";
RegistryKey adaskey = Registry.LocalMachine.OpenSubKey(lsbkey, false);
try
{
object regip = adaskey.GetValue("IP");
object regport = adaskey.GetValue("PORT");
localip = regip.ToString();
localport = int.Parse(regport.ToString());
}
catch (NullReferenceException ne)
{
localip = null;
localport = 0;
writelog(#"Aborting Service, IP or PORT doesn't exist in \local machine\software\mycompany\adas : "+ne.Message);
status = 0;
}
I use the Settings.settings stuff for storing the config for my services and I've had no problems. Just the usual that the user settings that are changed will be stored in it's usual obscure location that you have to hunt around for if you want to edit them by hand.
I dont see any reason not to use Settings in the properties of the project as you would for a winForms app. We do this, and it works fine.
I want to turn a program I have into a service so I can use it without logging it. Basically what it does is it backs up specified folders to a specified location using SSH. However the problem I'm running into is I don't know how to tell it these items. I only know how to start, stop, and run a custom command with only an integer with a parameter.
How can I do this?
Windows Service, not a Web Service
edit: The folders it backs up will not remain consistent and will be updated at every runtime
You can instantiate your service and pass command line arguments using the ServiceController class.
using (ServiceController serviceController = new ServiceController(serviceName))
{
string[] args = new string[1];
args[0] = "arg1";
serviceController.Start(args);
}
"arg1" will then be available as regular command line arguments in main() when Windows starts up the service.
I see that you (or someone) voted Sebastian Sedlak's answer down, because he mentioned hosting a WCF Service in the Windows Service. Your reply was
It's in nice bold lettering in the question. Not a Web Service, therefor WCF is out of the question
I think you misunderstood what he meant. He wasn't talking about a Web Service. He was talking about hosting a WCF Service within your Windows Service.
It's far from the same thing. You can host a WCF Service within any Windows (Forms/Console/Service) application. The point of doing so, is that the application is then reachable for communciation via its internal WCF Service, in the same fashion as you can communicate with a Web Service (you can also host WCF Services in IIS, btw, which would then make them "Web Services", in the sense you seem to be referring to).
In a Windows Service, this means you can send any command to it and also get any information you want from it - while it's running.
In fact, I am working on a project right now, which is a Windows Service that I need to be able to contact and pass commands to - and get information from - at runtime. For example, I want to be able to tell it where to store certain things, what to log, to have it reset/restart - and poll it for status messages. I do this by hosting a WCF Service inside the Windows Service. That WCF Service exposes a set of methods, that in my case includes receiving commands and returning status information. So when the Windows Service is running, I can contact it (even remotely), via its built-in WCF Service and tell it what to do.
This an extremely easy thing to implement, and in the case of Windows Services, can provide you with a much richer interface to the Service than through the basic standard commands.
However, you specified that you wanted your Windows Service to receive its folder settoings each time it starts up, which makes such a passive setup less than ideal (as it would be unable to do anything until you passed it the right folders).
One way to deal with this (using a hosted WCF Service), would be to have the Windows Service running all the time (i.e. automatic startup). Its default state would be idle. Then you could issue it a "start processing"-command, feeding it the correct folders to work on (through a call to the corresponding WCF Service method). Similarly, the WCF Service would expose methods giving you the status of the application (current folder, progress, busy/idle etc). Once the processing is done, it would go back into the idle state, waiting for the next set of folders to be supplied to it.
Doing it this way would make it very easy to control remotely - you could even make an online administration panel for it, accessible from anywhere.
The issue, is that, while passing in parameters is not difficult, when the machine restarts and windows tries to restart the service, those parameters are not there. they only exist when someone starts the service from the command line.
for example. I have a windows service which hosts a WCF service. I want the users to be able to specify a non-default port number for the WCF service to listen on. They do this by starting the windows service like so... MyService -port:xxxxx
Which works fine, until the server is rebooted, then windows restarts MyService (but without parameters) and the wcf service defaults to original port #
Any service is capable of receiving command line arguments at start-up.
Would it be possible to use a configuration file to specify these items?
Store the service's startup parameters in the registry: and then, when the registry starts, it should read its startup parameters from the registry.
Windows services have executables like any other. I believe you can write it to accept command-line parameters and specify those parameters in the Windows Service configuration. You can also have it read a config file. If you're using .NET, there are config file classes in the framework.
Why not just Host a WCF Service in the Windows Service to obatain such "admin" functions?
(Remoting is also possible)
RE: config file.
Of course a config file can be used.
And the file can be changed while the service is running.
This would be a nice solution if the config file changes in fact.
All my services use an XML config file, wrapped in a class for easy reuse.
The wrapper has an option to monitor the XML file using fileMonitor for changes, optionally refreshing the content of the config file automatically, and finally raises an event to the service class.
The service then has the option of "resetting" itself as needed to incorporate the new values in the XML configuration file.
Placing configuration into the registry has a few issues:
Security (ie: installer being granted access), depending on what tree is used
The service will not be aware of changes
Portability - although minor as the install should setup registry settings
Where an XML file is easy to copy, edit, share, view and understand.
Throw in some good COMMENT blocks and a detailed XSD file, and it becomes a source of good documentation too.
Look into XPath for easy navigation and extraction of values within the XML file.
$0.02
... david ...
Concerning the app.config file - I'm rather sure that your service will read and use those files as I write all my windows-services this way ;)
So just put everything you need in the app.config under "application" (not user) and put allway edit the "yourname.exe.config" in the folder where you "InstallUtil" the service from.