Skip to content

Instantly share code, notes, and snippets.

@sjwaight
Last active August 8, 2017 04:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sjwaight/984dff1eb48c55eac46ef3c1aec0f1a2 to your computer and use it in GitHub Desktop.
Save sjwaight/984dff1eb48c55eac46ef3c1aec0f1a2 to your computer and use it in GitHub Desktop.
Sample startup code showing how we can configure a VMSS instance based on its hostname and role.
namespace MyDemo.Website.WebApi
{
public class WebApiApplication : System.Web.HttpApplication
{
// This value is used if for some reason the configuration doesn't have a defined
// application insights instrumentation key
private const string DefaultAppInsightsKey = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
// Yes, these are hardcoded - need to change? Recompile and build VM Image and redeploy.
private const string ConfigurationDbId = "apiconfigs";
private const string ConfigurationDbCollection = "apiconfigentries";
private const string ServiceIdentifier = "webapi";
private static Dictionary<string, string> instanceTelemetry;
internal static string LoggingDatabaseCollection;
internal static string LoggingDatabaseKey;
internal static string LoggingDatabase;
internal static string LoggingDatabaseCollection;
internal static string ConfigDatabaseAccount;
internal static string ConfigDatabaseKey;
protected void Application_Start()
{
ApplyConfiguration();
// removed other initialisation code
}
private void ApplyConfiguration()
{
// Assumes you correctly version your assemblies... you do, don't you? :)
instanceTelemetry = new Dictionary<string, string>
{
{ "Version", Assembly.GetExecutingAssembly().GetName().Version.ToString() },
{ "Hostname", Environment.MachineName }
};
// Assumption that VM is an instance in an Azure VM Scale Set where
// instance names end with a 6 character hexatrigesimal value to uniquely identify them.
var instanceKey = Environment.MachineName.Substring(0, Environment.MachineName.Length - 6); // strip last 6 characters
// Secret URI in keyvault that will contain config DB information.
// Yes, the KeyVault instance is hardcoded which requires the solution to be re-compiled and re-deployed if
// changed. This is on purpose to stop people editing configuration on running instances. They
// should be editing the source code and pushing through the CD pipeline to re-bake the VM image.
var configDbSecretUri = $"https://yourkeyvaultinstance.vault.azure.net/secrets/{instanceKey}-{ServiceIdentifier}-configdb-account/";
var configDbKeySecretUri = $"https://yourkeyvaultinstance.vault.azure.net/secrets/{instanceKey}-{ServiceIdentifier}-configdb-key/";
// Call Key Vault and retrieve secret.
// Uses an Azure AD Service Principal to access
var keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(KevVaultUtils.GetToken));
// Configuration DB account and key read from KeyVault.
ConfigDatabaseAccount = keyVaultClient.GetSecretAsync(configDbSecretUri).Result.Value;
ConfigDatabaseKey = keyVaultClient.GetSecretAsync(configDbKeySecretUri).Result.Value;
var configResult = new ApiConfiguration();
try
{
// DocumentDbClient is just a helper class that provides a singleton client class I can use.
configResult = DocumentDbClient.ConfigInstance.CreateDocumentQuery<ApiConfiguration>(
UriFactory.CreateDocumentCollectionUri(ConfigurationDbId, ConfigurationDbCollection), new FeedOptions { MaxItemCount = 1 })
.Where(ac => ac.Id == $"{instanceKey}-{ServiceIdentifier}" && ac.AccessKey == ConfigDatabaseKey).AsEnumerable().FirstOrDefault();
if (!string.IsNullOrWhiteSpace(configResult.Id))
{
// Default fallback App Insights instance list above.
// This means even if we have a misconfiguration we should get telemetry *somewhere*
TelemetryConfiguration.Active.InstrumentationKey = string.IsNullOrWhiteSpace(configResult.AppInsightsKey) ? DefaultAppInsightsKey : configResult.AppInsightsKey;
// Our runtime values that can be used by our code
LoggingDatabaseAccount = configResult.LoggingDatabaseAccount;
LoggingDatabaseKey = configResult.LoggingDatabaseKey;
LoggingDatabase = configResult.LoggingDatabase;
LoggingDatabaseCollection = configResult.LoggingDatabaseCollection;
}
else
{
throw new ConfigurationErrorsException("Service is not configured correctly.");
}
}
catch (Exception ex)
{
AppInsightsClient.ClientInstance.TrackException(ex, instanceTelemetry);
// Bubble Exception
throw;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment