Thursday, April 3, 2014

Customsing New Relic installation during Azure deployments

For about a year we've been running New Relic to monitor our WebRoles running on the Azure platform. Installing has been quite simple by following the instructions initially found on the New Relic site and is now available via Nuget; however two things about this process have been irking me.

First, I wanted to be able to distinguish the CI and Production deployments in the New Relic portal by making them have different names, but the name as it appears in the New relic portal is controlled through a setting in the web.config and cannot be controlled though the Azure portal.

Second, I wanted to be able to control the licence key we used for CI (free licence, limited functionality) and Production (expensive licence, full functionality) deployments, however the key is embedded in the newrelic.cmd and is applied when the New Relic agent is installed; this is not easy to change during/post deployment.

The initial solution to both these problems involved producing two packages, one for the CI environment(s) and one for the Production environment. Instead of the normal Debug and Release build outputs, a 3rd target, Production, was used and the web.config was modified during the build process using a transform that changed the name to what was wanted. The licence key issue was resolved by have two newrelic.cmd items in the project and then packaging the required one with the appropriate build. This was not ideal but it worked in a fashion however the ProdOps guys were keen on having control over the name and licence key used in production.

Changing the Application name

New Relic gets the Application name from a setting in the web.config and so what is necessary is to read a setting in the Azure configuration and update the web.config. There are many ways to resolve this issue but the approach we took was based on the solution to an identical issue raised on GitHub.  

Form completeness I will however reiterate the steps below:

  1. In the ServiceDefinition.csdef file add a setting to the  <ConfigurationSettings/> section

  2. <ConfigurationSettings>
      <Setting name="NewRelicApplicationName" />
    </ConfigurationSettings>
    

  3. In the ServiceConfiguration file for your environment add a setting that will be used to set the Application name in New Relic

  4. <ConfigurationSettings>
      <Setting name="NewRelicApplicationName" value="MyApplication" />
    </ConfigurationSettings>
    

  5. In the WebRole.cs file for your application amend your code with the following

  6.     public class WebRole : RoleEntryPoint
        {
            public override bool OnStart()
            {
                ConfigureNewRelic();
    
                return base.OnStart();
            }
    
            private static void ConfigureNewRelic()
            {
                if (RoleEnvironment.IsAvailable && !RoleEnvironment.IsEmulated)
                {
                    string appName;
                    try
                    {
                        appName = RoleEnvironment.GetConfigurationSettingValue("NewRelicApplicationName");
                    }
                    catch (RoleEnvironmentException)
                    {
                        /*nothing we can do so just return*/
                        return;
                    }
    
                    if (string.IsNullOrWhiteSpace(appName))
                        return;
    
                    using (var server = new ServerManager())
                    {
                        // get the site's web configuration
                        const string siteNameFromServiceModel = "Web";
                        var siteName = string.Format("{0}_{1}", RoleEnvironment.CurrentRoleInstance.Id, siteNameFromServiceModel);
                        var siteConfig = server.Sites[siteName].GetWebConfiguration();
    
                        // get the appSettings section
                        var appSettings = siteConfig.GetSection("appSettings").GetCollection();
                        AddConfigElement(appSettings, "NewRelic.AppName", appName);
                        server.CommitChanges();
                    }
                }
            }
    
            private static void AddConfigElement(ConfigurationElementCollection appSettings, string key, string value)
            {
                if (appSettings.Any(t => t.GetAttributeValue("key").ToString() == key))
                {
                    appSettings.Remove(appSettings.First(t => t.GetAttributeValue("key").ToString() == key));
                }
                
                ConfigurationElement addElement = appSettings.CreateElement("add");
                addElement["key"] = key;
                addElement["value"] = value;
                appSettings.Add(addElement);
            }
        }
    
And that should be it

Changing the New Relic licence key

The New Relic licence key is applied when the New Relic agent is installed on the host so what we is needed is to read the Azure configuration when the newrelic.bat is executed as part of the Startup tasks (defined in the ServiceDefinition.csdef) and apply it when the agent is installed. There does not appear to be way of changing the licence key if your agents have already been installed other than reducing the number of instances to 0 and then scaling back up (I suggest you use the staging slot for this).

  1. In the ServiceDefinition.csdef file add a setting to the  <ConfigurationSettings/> section

  2. <ConfigurationSettings>
      <Setting name="NewRelicLicenceKey" />
    </ConfigurationSettings>
    

    and add a new Environment variable to the newrelic.cmd startup task that will be set by the new configuration setting

    <Task commandLine="newrelic.cmd" executionContext="elevated" taskType="simple">
            <Environment>
              <Variable name="EMULATED">
                <RoleInstanceValue xpath="/RoleEnvironment/Deployment/@emulated" />
              </Variable>
              <Variable name="NewRelicLicence">
                <!-- http://msdn.microsoft.com/en-us/library/windowsazure/hh404006.aspx -->
                <RoleInstanceValue xpath="/RoleEnvironment/CurrentInstance/ConfigurationSettings/ConfigurationSetting[@name='NewRelicLicenceKey']/@value" />
              </Variable>
              <Variable name="IsWorkerRole" value="false" />
            </Environment>
          </Task>
    

  3. In the ServiceConfiguration file for your environment add a setting that will be used to set the Application name in New Relic

  4. <ConfigurationSettings>
      <Setting name="NewRelicLicenceKey" value="<ADD YOUR KEY HERE>" />
    </ConfigurationSettings>

  5. Edit your newrelic.cmd to use the Environment variable

  6. :: Update with your license key
    SET LICENSE_KEY=%NewRelicLicenceKey%

Now you should be able to control the New Relic licence key during your deployment.