87

For two environments, local and cloud, how would I set up custom settings or parameters for resources such as Sql databases, storage accounts, etc... Ideally it would be one parameter name called in code to say, point a DbContext towards a particular database, that in configurations for either a local or cloud environment be different. Thank you.

StampyTurtle
  • 2,728
  • 2
  • 14
  • 16
  • Although I wish they included the application code for actually utilizing the config, Microsoft does show you to set it up in the following article: https://learn.microsoft.com/en-us/azure/service-fabric/service-fabric-how-to-parameterize-configuration-files – Adam Plocher Oct 11 '19 at 16:07

3 Answers3

151

In order to have per environment variables for running Service Fabric locally and in the cloud this is what you must do:

  1. Add your custom config section and parameters to the Settings.xml file of the Service/Actor project (located at \PackageRoot\Config\Settings.xml from the project root). Leave the parameters blank as we will be setting these elsewhere per environment. Here is an example one.
<?xml version="1.0" encoding="utf-8" ?>
<Settings xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2011/01/fabric">
<!-- Add your custom configuration sections and parameters here -->
    <Section Name="UserDatabase">
        <Parameter Name="UserDatabaseConnectionString" Value="" />
    </Section>
</Settings>
  1. In the ApplicationManifest.xml file of your Service Fabric project, there will be <ServiceManifestImport> elements for each of your included projects. Underneath that will be a <ConfigOverrides> element where we will declare what values for our configs will be supplanted by values set per environment in the local and cloud xml files underneath ApplicationParameters in our Service Fabric project. In that same ApplicationManifest.xml file, you'll need to add the parameter that will be present in the local and cloud xml files, otherwise they'll be overwritten upon build.

Continuing with the example above, this is how it would be set.

<Parameters>
    <Parameter Name="ServiceName_InstanceCount" DefaultValue="-1" />
    <Parameter Name="UserDatabaseConnectionString" DefaultValue="" />
</Parameters>
<ConfigOverrides>
    <ConfigOverride Name="Config">
        <Settings>
            <Section Name="UserDatabase">
                <Parameter Name="UserDatabaseConnectionString" Value="[UserDatabaseConnectionString]" />
            </Section>
        </Settings>
    </ConfigOverride>
</ConfigOverrides>
  1. In the local.xml and cloud.xml files underneath ApplicationParameters in your Service Fabric project, you will specify your environment specific variables like so.
<?xml version="1.0" encoding="utf-8"?>
<Application xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Name="fabric:/AppFabricName.ServiceFabric" xmlns="http://schemas.microsoft.com/2011/01/fabric">
    <Parameters>
        <Parameter Name="ServiceName_InstanceCount" Value="1" />
        <Parameter Name="UserDatabaseConnectionString" Value="Server=(localdb)\MsSqlLocalDb;Database=Users;User=ReadOnlyUser;Password=XXXXX;" />
    </Parameters>
</Application>
  1. Finally, in your Service/Actor you can access these per-environment configuration variables like so.
var configurationPackage = Context.CodePackageActivationContext.GetConfigurationPackageObject("Config");

var connectionStringParameter = configurationPackage.Settings.Sections["UserDatabase"].Parameters["UserDatabaseConnectionString"];
StampyTurtle
  • 2,728
  • 2
  • 14
  • 16
  • 106
    Can I just say "yuck!". This is hopelessly convoluted for a simple environment based setting. This is ripe for some dev effort from the SF team. – BrettRobi May 31 '16 at 22:54
  • Not sure what I'm missing, but my Context doesn't have a CodePackageActivationContext. I see in my stateless services that it gets passed in on the constructor to OwinCommunicationListener. But I'm not sure where to get it in the Actor? – Steve Jun 16 '16 at 14:35
  • 8
    Asked prematurely. The comments here: https://azure.microsoft.com/en-us/documentation/articles/service-fabric-manage-multiple-environment-app-configuration/ Point to using this: CodePackageActivationContext activationContext = FabricRuntime.GetActivationContext(); – Steve Jun 16 '16 at 14:44
  • When I do this every time i build my solution the Local.xml and Cloud.xml files get rewritten and lose all my parameters, do you know why? – Juan Zamudio Aug 04 '16 at 16:31
  • In the Service Fabric project itself or in the service project? – StampyTurtle Aug 04 '16 at 17:52
  • @Iain Yes that was it, I was reading the documentation and then found this answer, doing this step by step makes it work – Juan Zamudio Aug 18 '16 at 15:19
  • So you cannot access app parameters from AppManifest in your service code directly? – filip Aug 18 '16 at 17:40
  • 14
    This is much better than the actual documentation, thank you! Also agreed it is very convoluted... fix this SF team! – naspinski Sep 15 '16 at 14:06
  • You can also use environment variables, which i feel fits better with most applications, see - http://stackoverflow.com/a/41565653/4079967 – Kevin Smith Jan 10 '17 at 09:43
  • So the question says "runtime", in the spirit of that - has anyone found good way to make configuration changes at runtime without redeploying (to update the config file)? – Steve Feb 27 '17 at 17:21
  • 2
    I was getting an issue where these settings weren't being overridden. You have to define the parameters above `ServiceManifestImport` (child of `ApplicationManifest`) but the `ConfigOverrides` have to go in it (child of `ServiceManifestImport`). – Mardoxx Apr 06 '17 at 10:38
  • Out of curiosity, are the values in Cloud.xml file automatically used when the SF application is deployed to an Azure Service Fabric cluster using powershell? In other words, if the application deployment process senses that the application is being deployed to Azure cluster, will it automagically use the values in Cloud.xml? Is there no way to tell it to use the values in some other ApplicationParameters file? – Tola Odejayi Jun 10 '17 at 15:36
  • Is the name `Config` in `` required as long as the `Settings.xml` is in the `Config/` directory? – orepor Jun 27 '17 at 12:15
  • Is there a way to get to these variables from a self hosted exe? It doesn't seem that you can get to "Context" – Steve Aug 22 '17 at 14:30
  • @orepor The `Name` attribute on the `ConfigOveride` element is mandatory. The application won't even compile if you exclude it. It even has compile-time checking for that.. – Alternatex Jun 28 '18 at 09:40
  • How does one inject a section into IOptions accessor? – CBC_NS Dec 09 '22 at 21:27
  • @Steve Yes, there's Azure App Configuration Storage which you can use to manage configurations and just load them runtime via Connection String. So technically you would only need to maintain the connection string in the app. – It's actually me Aug 11 '23 at 04:32
42

You can just use environment variables just like any other application, this also works with guest executable within service fabric unlike the settings.xml as this requires the built-in service fabric runtime.

Within your application you can access environment variables just like any other .net application though the GetEnvironmentVariable method on the Environment class:

var baseUri = Environment.GetEnvironmentVariable("SuperWebServiceBaseUri");

Then we need to setup some default environment variables values, this is done within the ServiceManifest.xml manifest file of the service.

<?xml version="1.0" encoding="utf-8" ?>
<ServiceManifest Name="MyServicePkg" Version="1.0.0" xmlns="http://schemas.microsoft.com/2011/01/fabric" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <!-- snip -->
    <CodePackage Name="Code" Version="1.0.0">
        <!-- snip -->
        <EnvironmentVariables>
            <EnvironmentVariable Name="SuperWebServiceBaseUri" Value="http://localhost:12345"/>
        </EnvironmentVariables>
    </CodePackage>
    <!-- snip -->
</ServiceManifest>

These environment variable can then be overridden within the ApplicationManifest.xml file by using the following code:

<?xml version="1.0" encoding="utf-8"?>
<ApplicationManifest xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ApplicationTypeName="ChileTargetType" ApplicationTypeVersion="1.0.0" xmlns="http://schemas.microsoft.com/2011/01/fabric">
    <Parameters>
        <!-- snip -->
    </Parameters>
    <ServiceManifestImport>
        <ServiceManifestRef ServiceManifestName="MyServicePkg" ServiceManifestVersion="1.0.0" />
        <EnvironmentOverrides CodePackageRef="Code">
            <EnvironmentVariable Name="SuperWebServiceBaseUri" Value="https://the-real-live-super-base-uri.com/"/>
        </EnvironmentOverrides>
    </ServiceManifestImport>
    <!-- snip -->
</ApplicationManifest>

This then can be parameterised like any other application manifest setting using the local.xml and cloud.xml.

<?xml version="1.0" encoding="utf-8"?>
<Application xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Name="fabric:/AppFabricName.ServiceFabric" xmlns="http://schemas.microsoft.com/2011/01/fabric">
    <Parameters>
        <Parameter Name="MyService_SuperWebServiceBaseUri" Value="https://another-base-uri.com/" />
    </Parameters>
</Application>

Then we'll have to update the ApplicationManifest.xml to support these parameters;

<?xml version="1.0" encoding="utf-8"?>
<ApplicationManifest xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ApplicationTypeName="ChileTargetType" ApplicationTypeVersion="1.0.0" xmlns="http://schemas.microsoft.com/2011/01/fabric">
    <Parameters>
        <Parameter Name="MyService_SuperWebServiceBaseUri" DefaultValue="https://the-real-live-super-base-uri.com/" />
    </Parameters>
    <ServiceManifestImport>
        <ServiceManifestRef ServiceManifestName="MyServicePkg" ServiceManifestVersion="1.0.0" />
        <EnvironmentOverrides CodePackageRef="Code">
            <EnvironmentVariable Name="SuperWebServiceBaseUri" Value="[MyService_SuperWebServiceBaseUri]"/>
        </EnvironmentOverrides>
    </ServiceManifestImport>
    <!-- snip -->
</ApplicationManifest>
Brad Wilson
  • 67,914
  • 9
  • 74
  • 83
Kevin Smith
  • 13,746
  • 4
  • 52
  • 77
9

The above answers explain well how it is done. I want to add a sidemark, why it is that 'convoluted':

It has to be this way, as the services are intended to be self-contained. They should run by default in any application they are linked into. Independent of the application's Manifest. So the service can only rely on parameters, which are at least predefined in its own configuration.

These presettings can then be overwritten by the application. This is the only universal approach.

BaluJr.
  • 1,010
  • 2
  • 11
  • 25