How to encrypt xml file in Azure Logicapp? - azure

I have a below requirement
1. XML file need to read from SharePoint and copy into Azure Blob.
2. Need to use Logic app to read the file from share point and copy into Azure Blob.
3. Before copy into Azure Blob need to encrypt the data in Logic app.
Please let me know if any suggestions.

As far as I know there is nothing out the box to help here. You will need to write an Azure Function (or a REST endpoint) to perform the encryption and call that from your Logic App.
There are many examples of such a function, you can find one for PGP here. The core function is copied below and Microsoft's guidance on calling functions from Logic Apps is here.
using System.IO;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using PgpCore;
using System.Threading.Tasks;
using Microsoft.Azure.KeyVault.Models;
using System.Net.Http;
using Microsoft.Azure.Services.AppAuthentication;
using Microsoft.Azure.KeyVault;
using System;
using System.Text;
using System.Collections.Concurrent;
using Microsoft.AspNetCore.Http.Internal;
using Microsoft.Extensions.Logging;
namespace AzureFunctionsPGPEncrypt
{
public static class PGPEncrypt
{
private static readonly HttpClient client = new HttpClient();
private static ConcurrentDictionary<string, string> secrets = new ConcurrentDictionary<string, string>();
[FunctionName(nameof(PGPEncrypt))]
public static async Task<IActionResult> RunAsync(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)]
HttpRequest req, ILogger log)
{
log.LogInformation($"C# HTTP trigger function {nameof(PGPEncrypt)} processed a request.");
string publicKeyBase64 = req.Query["public-key"];
string publicKeyEnvironmentVariable = req.Query["public-key-environment-variable"];
string publicKeySecretId = req.Query["public-key-secret-id"];
if (publicKeyBase64 == null && publicKeyEnvironmentVariable == null && publicKeySecretId == null)
{
return new BadRequestObjectResult("Please pass a base64 encoded public key, an environment variable name, or a key vault secret identifier on the query string");
}
if (publicKeyBase64 == null && publicKeyEnvironmentVariable != null)
{
publicKeyBase64 = Environment.GetEnvironmentVariable(publicKeyEnvironmentVariable);
}
if (publicKeyBase64 == null && publicKeySecretId != null)
{
try
{
publicKeyBase64 = await GetPublicKeyAsync(publicKeySecretId);
}
catch (KeyVaultErrorException e) when (e.Body.Error.Code == "SecretNotFound")
{
return new NotFoundResult();
}
catch (KeyVaultErrorException e) when (e.Body.Error.Code == "Forbidden")
{
return new UnauthorizedResult();
}
}
byte[] data = Convert.FromBase64String(publicKeyBase64);
string publicKey = Encoding.UTF8.GetString(data);
req.EnableRewind(); //Make RequestBody Stream seekable
Stream encryptedData = await EncryptAsync(req.Body, publicKey);
return new OkObjectResult(encryptedData);
}
private static async Task<string> GetPublicKeyAsync(string secretIdentifier)
{
if (!secrets.ContainsKey(secretIdentifier))
{
var azureServiceTokenProvider = new AzureServiceTokenProvider();
var authenticationCallback = new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback);
var kvClient = new KeyVaultClient(authenticationCallback, client);
SecretBundle secretBundle = await kvClient.GetSecretAsync(secretIdentifier);
secrets[secretIdentifier] = secretBundle.Value;
}
return secrets[secretIdentifier];
}
private static async Task<Stream> EncryptAsync(Stream inputStream, string publicKey)
{
using (PGP pgp = new PGP())
{
Stream outputStream = new MemoryStream();
using (inputStream)
using (Stream publicKeyStream = GenerateStreamFromString(publicKey))
{
await pgp.EncryptStreamAsync(inputStream, outputStream, publicKeyStream, true, true);
outputStream.Seek(0, SeekOrigin.Begin);
return outputStream;
}
}
}
private static Stream GenerateStreamFromString(string s)
{
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(s);
writer.Flush();
stream.Position = 0;
return stream;
}
}
}

Related

How can I create a zip file from array variable [fileName and FileContent]. using azure function or Azure logic app(with out any third part service)

How can I create a zip file from array variable [{"fileName":"", "FileContent" :""}]. using azure function or Azure logic app(with out any third part service)
For now, Compress/Zip files is not supported in logic app(if we do not use third party service), you could upvote for this feature on feedback page.
But for azure function, we can implement it by code without third party service. I wrote a sample in my function, please refer to my function code below:
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.IO.Compression;
namespace FunctionApp1
{
public static class Function1
{
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
FileItem[] data = JsonConvert.DeserializeObject<FileItem[]>(requestBody);
//As the request data you provided is a array with multiple file items, so here use foreach to loop each file item.
foreach (FileItem item in data)
{
log.LogInformation(item.fileName);
log.LogInformation(item.FileContent);
using (var memoryStream = new MemoryStream())
{
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
var demoFile = archive.CreateEntry(item.fileName + ".txt");
using (var entryStream = demoFile.Open())
using (var streamWriter = new StreamWriter(entryStream))
{
streamWriter.Write(item.FileContent);
}
}
//here I create the zip file in local, you can modify the code to create the zip file anywhere else you want.
using (var fileStream = new FileStream(#"D:\Temp\" + item.fileName + ".zip", FileMode.Create))
{
memoryStream.Seek(0, SeekOrigin.Begin);
memoryStream.CopyTo(fileStream);
}
}
}
string responseMessage = "complete";
return new OkObjectResult(responseMessage);
}
}
public class FileItem
{
public string fileName { get; set; }
public string FileContent { get; set; }
}
}
Running the function above and request it in postman.
Then we can see the zip file was created in the path which I specified in code.

Performance of Azure SDK v12 vs Storage Data Movement Library?

I know that the Storage Data Movement Library is supposed to be faster when uploading and downloading files to and from blob storage, but I am not seeing the performance benefits of it when compared to Azure SDK v12. I got an average of 37.463 seconds with Azure SDK v12 and 41.863 seconds using Storage Data Movement Library (SDML).
Here is the code using SDML:
namespace FunctionApp
{
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Storage;
using Microsoft.Azure.Storage.Blob;
using Microsoft.Azure.Storage.DataMovement;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
public static class Function1
{
[FunctionName("A")]
public static async Task<IActionResult> HttpStart(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "testRoute")] HttpRequestMessage req,
ILogger log)
{
Stopwatch timer = new Stopwatch();
timer.Start();
try
{
ServicePointManager.Expect100Continue = false;
ServicePointManager.DefaultConnectionLimit = Environment.ProcessorCount * 8;
TransferManager.Configurations.ParallelOperations = 64;
string fileToDownload = "<URI to zip file in blob storage containing two 300MB files";
string connectionString = "<connection string to storage account>";
string containerName = "<container to upload files to>";
using MemoryStream test = new MemoryStream();
CloudBlockBlob sourceBlob = new CloudBlockBlob(new Uri(fileToDownload));
await TransferManager.DownloadAsync(sourceBlob, test);
CloudStorageAccount account = CloudStorageAccount.Parse(connectionString);
CloudBlobClient blobClient = account.CreateCloudBlobClient();
CloudBlobContainer container = blobClient.GetContainerReference(containerName);
using ZipArchive zipArchive = new ZipArchive(test);
foreach (ZipArchiveEntry file in zipArchive.Entries)
{
if (!string.IsNullOrEmpty(file.Name))
{
CloudBlockBlob destBlob = container.GetBlockBlobReference(file.FullName);
using Stream stream = file.Open();
await TransferManager.UploadAsync(stream, destBlob);
}
}
}
catch (Exception exception)
{
return new InternalServerErrorResult();
}
timer.Stop();
return new OkObjectResult(timer.ElapsedMilliseconds);
}
}
}
Here is the code using Azure SDK v12:
namespace FunctionApp
{
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Specialized;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
public static class Function1
{
[FunctionName("A")]
public static async Task<IActionResult> HttpStart(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "testRoute")] HttpRequestMessage req,
ILogger log)
{
Stopwatch timer = new Stopwatch();
timer.Start();
try
{
ServicePointManager.Expect100Continue = false;
ServicePointManager.DefaultConnectionLimit = Environment.ProcessorCount * 8;
string fileToDownload = "<URI to zip file in blob storage containing two 300MB files";
string connectionString = "<connection string to storage account>";
string containerName = "<container to upload files to>";
using MemoryStream test = new MemoryStream();
BlockBlobClient client = new BlockBlobClient(new Uri(fileToDownload));
await client.DownloadToAsync(test);
BlobContainerClient containerClient = new BlobContainerClient(connectionString, containerName);
using ZipArchive zipArchive = new ZipArchive(test);
foreach (ZipArchiveEntry file in zipArchive.Entries)
{
if (!string.IsNullOrEmpty(file.Name))
{
BlockBlobClient blockBlobClient = containerClient.GetBlockBlobClient(file.FullName);
using Stream stream = file.Open();
await blockBlobClient.UploadAsync(stream);
}
}
}
catch (Exception exception)
{
return new InternalServerErrorResult();
}
timer.Stop();
return new OkObjectResult(timer.ElapsedMilliseconds) ;
}
}
}
For Data Movement library, you may set ParallelOperations and BlockSize, like below:
TransferManager.Configurations.ParallelOperations = 20;
TransferManager.Configurations.BlockSize = 20971520*2; //20M
I did the test at my side, SDML is more faster.

How to parse Excel spreadsheets in Azure Logic Apps

I need to parse and extract Column information from an Excel spreadsheet using Azure Logic Apps
I have already setup the ability for my Logic App to retrieve the latest unread Emails from my Outlook. Also, my Logic App does a FOR EACH to read all attachments (from unread emails) and make sure they are Excel files (based on filename extension).
I have a basic Excel file that contains 3 columns "Product, Description, Price" I need to parse each row (only Product and Price) column.
I will add the ability to store that parsed into into my SQL table hosted on Azure.
Here you go, you could use HTTP Request as well.
using ExcelDataReader;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using Nancy.Json;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace ConvertExcelToJSon
{
public static class Function1
{
[FunctionName("ConvertToJson")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
string blobName = data?.blobName;
string[] splitBlob = blobName.Split('/');
Stream blob = await GetBlobStreamAsync(splitBlob[1], splitBlob[2] + "/" + splitBlob[3]);
DataSet ds = CreateDataSet(blob);
List<Simple> simpleList = new List<Simple>();
foreach (DataTable table in ds.Tables)
{
return (ActionResult)new OkObjectResult(DataTableToJSON(table));
}
return blobName != null
? (ActionResult)new OkObjectResult($"Hello, {blobName}")
: new BadRequestObjectResult("Please pass a name on the query string or in the request body");
}
public static string DataTableToJSON(DataTable table)
{
List<Dictionary<string, object>> list = new List<Dictionary<string, object>>();
foreach (DataRow row in table.Rows)
{
Dictionary<string, object> dict = new Dictionary<string, object>();
foreach (DataColumn col in table.Columns)
{
dict[col.ColumnName] = (Convert.ToString(row[col]));
}
list.Add(dict);
}
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize(list);
}
public static string AppSetting(this string Key)
{
string ret = string.Empty;
if (Environment.GetEnvironmentVariable(Key) != null)
{
ret = Environment.GetEnvironmentVariable(Key);
}
return ret;
}
public static async Task<Stream> GetBlobStreamAsync(string containerName, string blobName)
{
Stream myBlob = new MemoryStream();
if (CloudStorageAccount.TryParse("AzureWebJobsStorage".AppSetting(), out CloudStorageAccount storageAccount))
{
CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer container = cloudBlobClient.GetContainerReference(containerName);
CloudBlob myBloab = container.GetBlobReference(blobName);
await myBloab.DownloadToStreamAsync(myBlob);
myBlob.Seek(0, SeekOrigin.Begin);
}
return myBlob;
}
public static DataSet CreateDataSet(Stream stream)
{
DataSet ds;
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
IExcelDataReader reader = null;
reader = ExcelReaderFactory.CreateOpenXmlReader(stream);
ds = reader.AsDataSet(new ExcelDataSetConfiguration()
{
ConfigureDataTable = (tableReader) => new ExcelDataTableConfiguration()
{
UseHeaderRow = true,
}
});
return ds;
}
}
}
I suggest you call an Azure Function from your logic App and use the Function to convert the Excel into a JSON Object. (I am currently doing this very succesfully) I use ExcelDataReader parse to parse a Blob that the Logic App creates. Send the blob location in the request and respond back with the JSON.

Can I set the access tier when I upload a blob? If yes, then how to do that?

I did not find any way to set the access tier of a blob when I upload it, I know I can set a blob's access tier after I uploaded it, but I just want to know if I can upload the blob and set it's access tier in only one step. And if there is any golang API to do that?
I googled it but I got nothing helpful till now.
Here is what I did now, I mean upload it and then set it's access tier.
// Here's how to upload a blob.
blobURL := containerURL.NewBlockBlobURL(fileName)
ctx := context.Background()
_, err = azblob.UploadBufferToBlockBlob(ctx, data, blobURL, azblob.UploadToBlockBlobOptions{})
handleErrors(err)
//set tier
_, err = blobURL.SetTier(ctx, azblob.AccessTierCool, azblob.LeaseAccessConditions{})
handleErrors(err)
But I want to upload a blob and set it's tier in one step, not two steps as I do now.
The short answer is No. According to the offical REST API reference, the blob operation you want is that to do via two REST APIs Put Blob and Set Blob Tier. Actually, all SDK APIs for different languages are implemented by wrapping the related REST APIs.
Except for Page Blob, you can set the header x-ms-access-tier in your operation request to do your want, as below.
For Block Blob, the operations in two steps are necessary, and can not be merged.
It is now possible using the new x-ms-access-tier header:
x-ms-access-tier
REST API with auth
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Mime;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace WhateverYourNameSpaceIs
{
class Program
{
private const string StorageKey = #"PutYourStorageKeyHere";
private const string StorageAccount = "PutYourStorageAccountHere";
private const string ContainerName = "PutYourContainerNameHere";
private const string Method = "PUT";
private const string ContentType = MediaTypeNames.Image.Jpeg;
private static readonly string BlobStorageTier = StorageTier.Cool;
private static readonly List<Tuple<string, string>> HttpContentHeaders = new List<Tuple<string, string>>()
{
new Tuple<string, string>("x-ms-access-tier", BlobStorageTier),
new Tuple<string, string>("x-ms-blob-type", "BlockBlob"),
new Tuple<string, string>("x-ms-date", DateTime.UtcNow.ToString("R")),
new Tuple<string, string>("x-ms-version", "2018-11-09"),
new Tuple<string, string>("Content-Type", ContentType),
};
static async Task Main()
{
await UploadBlobToAzure("DestinationFileNameWithoutPath", "LocalFileNameWithPath");
}
static async Task<int> UploadBlobToAzure(string blobName, string fileName)
{
int returnValue = (int)AzureCopyStatus.Unknown;
try
{
using var client = new HttpClient();
using var content = new ByteArrayContent(File.ReadAllBytes(fileName));
HttpContentHeaders.ForEach(x => content.Headers.Add(x.Item1, x.Item2));
var stringToSign = $"{Method}\n\n\n{content.Headers.ContentLength.Value}\n\n{ContentType}\n\n\n\n\n\n\n";
foreach (var httpContentHeader in HttpContentHeaders.Where(x => x.Item1 != "Content-Type").OrderBy(x => x.Item1))
stringToSign += $"{httpContentHeader.Item1.ToLower()}:{httpContentHeader.Item2}\n";
stringToSign += $"/{StorageAccount}/{ContainerName}/{blobName}";
HMACSHA256 hmac = new HMACSHA256(Convert.FromBase64String(StorageKey));
string signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("SharedKey", $"{StorageAccount}:{signature}");
var httpResponse = await client.PutAsync($"https://{StorageAccount}.blob.core.windows.net/{ContainerName}/{blobName}", content);
returnValue = (int)httpResponse.StatusCode;
}
catch (IOException ioException)
{
Console.WriteLine(ioException.ToString());
returnValue = (int)AzureCopyStatus.FileNotFound;
}
catch (Exception exception)
{
Console.WriteLine(exception.ToString());
returnValue = (int)AzureCopyStatus.Error;
}
return returnValue;
}
internal enum AzureCopyStatus
{
Unknown = -1,
Error = 0,
FileNotFound = 2
}
internal static class StorageTier
{
internal static string Cool = "Cool";
internal static string Hot = "Hot";
}
}
}

How to get full list of CloudConfiguration from inside a web service at runtime?

ConfigurationManager has AppSettings name-value collection but CloudConfigurationManager has only GetSetting(string) method where you can get the config settings 1 by 1 if you know the key.
Is there a way to get the whole config of the role runtime?
The root cause is that I want to make strong typed configuration in order to abstract it away and make my code more testable. Using CloudConfigurationManager directly is implicit dependency which I want to remove with an abstraction which I want to stub in tests. So I find this practical. Which brings me to my question.
I do not want to use library like fx.configuration.azure because I will have to carry its dependency altogether because it requires inheritance of a base class.
AFAIK, there's no direct method available which will give you this information.
However there's a workaround that you can use. It involves making use of Service Management API's Get Deployment operation. This operation will return an XML and one of the element there is Configuration which contains your service configuration file in Base64 encoded format. You can read this element, convert it into string and parse the XML to get to ConfigurationSettings elements. It's child elements contains all the settings.
For this, you could either write your own wrapper over Service Management REST API or make use of Azure Management Library.
UPDATE
So here's a sample code for listing all configuration settings from Service Configuration File using Azure Management Library. It's a simple console app hacked together in very short amount of time thus has a lot of scope of improvement :). For management certificate, I have used the data from Publish Setting File.
You just have to install Azure Management Library Nuget Package in your console application:
Install-Package Microsoft.WindowsAzure.Management.Libraries
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Management.Compute;
using System.Security.Cryptography.X509Certificates;
using System.Xml.Linq;
namespace ReadConfigurationSettingsUsingAzureManagementLibrary
{
class Program
{
static string subscriptionId = "<subscription-id>";
static string managementCertContents = "<Base64 Encoded Management Certificate String from Publish Setting File>";//Certificate string from Azure Publish Settings file
static string cloudServiceName = "<your cloud service name>";
static string ns = "http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration";
static void Main(string[] args)
{
var managementCetificate = new X509Certificate2(Convert.FromBase64String(managementCertContents));
var credentials = new CertificateCloudCredentials(subscriptionId, managementCetificate);
var computeManagementClient = new ComputeManagementClient(credentials);
var response = computeManagementClient.HostedServices.GetDetailed(cloudServiceName);
var deployment = response.Deployments.FirstOrDefault(d => d.DeploymentSlot == Microsoft.WindowsAzure.Management.Compute.Models.DeploymentSlot.Production);
if (deployment != null)
{
var config = deployment.Configuration;
XElement configXml = XElement.Parse(config);
var roles = configXml.Descendants(XName.Get("Role", ns));
foreach (var role in roles)
{
Console.WriteLine(role.Attribute("name").Value);
Console.WriteLine("-----------------------------");
var configurationSettings = role.Element(XName.Get("ConfigurationSettings", ns));
foreach (var element in configurationSettings.Elements(XName.Get("Setting", ns)))
{
var settingName = element.Attribute("name").Value;
var settingValue = element.Attribute("value").Value;
Console.WriteLine(string.Format("{0} = {1}", settingName, settingValue));
}
Console.WriteLine("==========================================");
}
}
Console.ReadLine();
}
}
}
Here is an updated implementation which takes care if you are running in emulator or not and if you are running in local web server or not. After returning the dictionary it can be easily abstracted away from the whole application by Castle.DictionaryAdapter. I shared the code as template project on GitHub here. Here is an excerpt:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.Xml;
using System.Xml.Linq;
using Castle.Components.DictionaryAdapter;
using Core.Configuration.Interfaces;
using Microsoft.Azure;
using Microsoft.WindowsAzure.Management.Compute;
using Microsoft.WindowsAzure.Management.Compute.Models;
using Microsoft.WindowsAzure.ServiceRuntime;
namespace Core.Configuration
{
public class AzureServiceConfigurationProvider : IAzureServiceConfigurationProvider
{
private readonly string _subscriptionId;
// The Base64 Encoded Management Certificate string from Azure Publish Settings file
// download from https://manage.windowsazure.com/publishsettings/index
private readonly string _managementCertContents;
private readonly string _cloudServiceName;
private readonly string _serviceConfigurationNamespace;
public DefaultAzureServiceConfigurationProvider(IWebConfigSettings webConfigSettings)
{
_subscriptionId = webConfigSettings.SubscriptionId;
_managementCertContents = webConfigSettings.ManagementCertContents;
_cloudServiceName = webConfigSettings.CloudServiceName;
_serviceConfigurationNamespace = webConfigSettings.ServiceConfigurationNamespace;
}
public Dictionary<string, Dictionary<string, string>> GetConfigRaw()
{
Trace.WriteLine("DefaultAzureServiceConfigurationProvider->GetConfigRaw->Start");
var configuration = new Dictionary<string, Dictionary<string, string>>();
var configXml = GetConfigXml();
Trace.WriteLine("DefaultAzureServiceConfigurationProvider->GetConfigRaw->XmlExtracted");
var roles = configXml.Descendants(XName.Get("Role", _serviceConfigurationNamespace));
Trace.WriteLine("DefaultAzureServiceConfigurationProvider->GetConfigRaw->Roles : ");
foreach(var role in roles)
{
var roleConfiguration = new Dictionary<string, string>();
var roleName = role.Attribute("name").Value;
Trace.WriteLine("DefaultAzureServiceConfigurationProvider->GetConfigRaw->RoleName : " + roleName);
var configurationSettings = role.Element(XName.Get("ConfigurationSettings", _serviceConfigurationNamespace));
if (configurationSettings == null)
{
throw new InvalidOperationException("configurationSettings is null");
}
foreach(var element in configurationSettings.Elements(XName.Get("Setting", _serviceConfigurationNamespace)))
{
var settingName = element.Attribute("name").Value;
var settingValue = element.Attribute("value").Value;
Trace.WriteLine("DefaultAzureServiceConfigurationProvider->GetConfigRaw->settingName : " + settingName + " settingValue : " + settingValue);
roleConfiguration.Add(settingName, settingValue);
}
configuration.Add(roleName, roleConfiguration);
}
return configuration;
}
public IAzureServiceConfiguration GetConfig()
{
var configFactory = new DictionaryAdapterFactory();
IAzureServiceConfiguration config;
try
{
var rawAzureServiceConfig = GetConfigRaw();
Trace.WriteLine("DefaultAzureServiceConfigurationProvider->GetConfig :");
var rawAzureWebServiceConfig = rawAzureServiceConfig["Core.Web"];
config = configFactory.GetAdapter<IAzureServiceConfiguration>(rawAzureWebServiceConfig);
config = ComplementConfigurationFromConfigurationManager(config);
}
catch(Exception exception)
{
// happens in some projects when using Full Emulator
// so we fallback to cloudconfigurationmanager
// this is not bad since we have isolated it in configuration assembly
Trace.WriteLine(exception.Message);
Trace.WriteLine(exception.StackTrace);
Hashtable hashConfig = GetConfigFromConfigurationManager();
config = configFactory.GetAdapter<IAzureServiceConfiguration>(hashConfig);
}
return config;
}
private IAzureServiceConfiguration ComplementConfigurationFromConfigurationManager(IAzureServiceConfiguration config)
{
Trace.WriteLine("Complementing configuration");
var azureConfigType = config.GetType();
foreach(PropertyInfo property in config.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly))
{
var xmlConfigValue = CloudConfigurationManager.GetSetting(property.Name);
var liveConfigPropValue = (string)azureConfigType.GetProperty(property.Name).GetValue(config, null);
if(string.IsNullOrEmpty(liveConfigPropValue))
{
Trace.WriteLine(property.Name + " in live config is empty. Complementing with '" + xmlConfigValue + "' from ConfigurationManager.");
property.SetValue(config, xmlConfigValue);
}
// do something with the property
}
return config;
}
private Hashtable GetConfigFromConfigurationManager()
{
Hashtable hashConfig = new Hashtable();
var configProperties = typeof(IAzureServiceConfiguration).GetProperties();
foreach(PropertyInfo prop in configProperties)
{
hashConfig.Add(prop.Name, CloudConfigurationManager.GetSetting(prop.Name));
}
return hashConfig;
}
private XElement GetConfigXml()
{
XElement configXml = null;
Trace.WriteLine("DefaultAzureServiceConfigurationProvider->GetConfigXml");
if(!RoleEnvironment.IsAvailable/*as local web project*/ || RoleEnvironment.IsEmulated /*as azure emulator project*/)
{
Trace.WriteLine("DefaultAzureServiceConfigurationProvider->GetConfigXml->!RoleEnvironment.IsAvailable || RoleEnvironment.IsEmulated");
try
{
var localConfigFile =
new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory).Parent.EnumerateFiles(
"*Local.cscfg", SearchOption.AllDirectories).FirstOrDefault();
XmlDocument doc = new XmlDocument();
doc.Load(localConfigFile.FullName);
configXml = XElement.Parse(doc.InnerXml);
}
catch(Exception exception) // happens in some projects when using Full Emulator
{
Trace.WriteLine(exception.Message);
Trace.WriteLine(exception.StackTrace);
throw; // intended - just marking - will catch it above
}
}
else
{
Trace.WriteLine("DefaultAzureServiceConfigurationProvider->GetConfigXml->RoleEnvironment ->in cloud");
var managementCertificate = new X509Certificate2(Convert.FromBase64String(_managementCertContents));
var credentials = new CertificateCloudCredentials(_subscriptionId, managementCertificate);
var computeManagementClient = new ComputeManagementClient(credentials);
var response = computeManagementClient.HostedServices.GetDetailed(_cloudServiceName);
var deployment = response.Deployments.FirstOrDefault(d => d.DeploymentSlot == DeploymentSlot.Production);
if(deployment != null)
{
var config = deployment.Configuration;
configXml = XElement.Parse(config);
}
}
return configXml;
}
}
internal static class TypeHelpers
{
public static bool IsNumber(this object value)
{
return value is sbyte
|| value is byte
|| value is short
|| value is ushort
|| value is int
|| value is uint
|| value is long
|| value is ulong
|| value is float
|| value is double
|| value is decimal;
}
public static bool IsString(this object value)
{
return value is string;
}
}
}

Resources