1

We are using TeamCity and Octopus Deploy to perform CI/CD with the custom WSP's that we use for SharePoint, and I'm currently trying to solve the problem of how to handle environment-specific configuration.

Consider the scenario where we have a WSP, and we add to it a web part which depends on some external web service. I have dev, test, and production environments, and each has a different URL for this web service. Where/how should I store the URL, and how can I set it up so that no manual steps are required to get the correct value saved in each environment?

I would prefer not to depend on anything pre-existing on the server (registry setting, environment name in a property bag, etc) as I'd like my WSP's to be as self-sufficient as possible.

0

I do not know Octopus Deploy, but I briefly tried TeamCity for this same purpose. The way we did it was to create Environment Variables per build configuration where we set the properties like Server URL etc.

When deploying we used PowerShell scripts that took parameters and in TeamCity, when calling this script we handed off the Environment Variable to it.

This way, your scripts stay generic (not specific to one server) and you can easily configure and reuse the Environment Variables for different Build Configurations (LAB\TEST\PROD).

EDIT: Let's get this variables all the way to your custom code

Now that you've handed off these variables to your deployment scripts, some of them will probably be used during deployment but others may only be needed at runtime of your code. In this case it's very common to use the Property Bag. Easy to set and read from at any time you have access to a SharePoint Context object.

  • We have used the property bag before, but it is a manual step to set the correct value using SharePoint Designer. I guess the way to automate this then would be to have a (different in each environment) PowerShell script set the property bag values before activating the features? I can see this working, my main issue with it would be scoping of configuration settings. We will probably have numerous WSPs and I don't want it to be difficult to avoid conflicting setting names. – jon without an h Feb 28 '14 at 15:36
  • Use a prefix to solve the naming conflicts (prefix with the wsp name abbreviation or something). You would only need 1 script since you've extract the actual variables to your CI environment. In case your development also needs these scripts (very likely), make a bat file or something on top of it with correct variables for each DEV's environment. If you think about it, there is no going around this, you will always have seperate files with values specific to each environment. Scoping wise, it's a good idea to keep everything at the SPSite level. – Cameron Verhelst Feb 28 '14 at 15:42
  • This approach looks like the easiest to automate of everything I've looked at. Thanks for the suggestion! – jon without an h Mar 3 '14 at 14:35
0

I'm currently leaning towards a solution involving a custom SPPersistedObject, as described here.

You create your own class that derives from the SPPersistedObject and add your settings as properties to the class and marks them with the Persisted attribute.

public class TheSettings: SPPersistedObject {
    public TheSettings() {}

    public TheSettings(string name, SPPersistedObject parent, Guid id) 
        : base(name, parent, id) { }

    [Persisted]
    public string WebServiceUrl;
}

Then you use the SPWebApplication GetChild method to retrieve your settings.

TheSettings settings = webApplication
    .GetChild<TheSettings>("theSettings");

I usually add two static methods to my settings class for easy creating and retrieval of settings:

public static TheSettings GetSettings(SPWebApplication webApplication) {
    TheSettings settings = webApplication.GetChild<TheSettings>("theSettings");
    if(settings == null) {
        return TheSettings.CreateNew(webApplication);
    }
    return settings;
}
public static TheSettings CreateNew(SPWebApplication webApplication) {
   return new TheSettings("theSettings",  webApplication, Guid.NewGuid());
}

I could create a custom Feature Receiver which has the ability to set different settings in different environments, but I still need some way at activation time to tell it which environment to use.

  • 1
    Is this an answer or an addition to your original question? If the latter, please edit your question instead :) – Robert Lindgren Feb 28 '14 at 15:23
  • Aha, I think I misinterpreted your question. You actually want to know how to get your external variables into your code, correct ? A very common (but not necesseraly the best) way to do this is to use the property bag of an SPWeb/SPSite object. They are rather easy to set and read and can be always accessed when you have a SharePoint Context available. – Cameron Verhelst Feb 28 '14 at 15:25
  • One thing I can warn you about is to stay away from Feature Receivers. They require manual changes to the Features Folder file in the Hive before changes can be applied (after deployment of the WSP) and are thus not very flexible. The Property bags are very flexibly, though they have their own problems, mainly concurrent writes. I believe in this case it is best practices to use your own SQL table and relevant code to read and write to it. – Cameron Verhelst Feb 28 '14 at 15:29
  • @RobertLindgren This is a potential answer, but has some blanks to fill in. I don't necessarily need to know how to get this particular approach to work, if there is a better way. – jon without an h Feb 28 '14 at 15:29
  • @CameronVerhelst So if I had a custom SQL table, where would it reside? And I would have environment-specific deployment scripts which insert/update the appropriate values in it? I can see this working but it seems very heavy-handed. – jon without an h Feb 28 '14 at 15:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.